-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Add minimum token permissions for all GitHub workflow files to improve security posture according to OpenSSF Scorecard recommendations.
Context
This addresses the Token-Permissions check from https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#token-permissions.
Step 1: Add Root-Level Permissions
Every workflow file MUST have a root-level permissions: YAML block.
Rules for Root-Level Permission YAML Block:
- 🔍 READ THE FILE FIRST: Always examine the existing formatting style before making any changes
- Root-Level permissions MUST be limited to either
read-allorcontents: read - If the existing rootl-level permission is
read-allthen leave it unchanged - If existing root-level permissions have more than read permissions, then move the root-level permission down to the job level where it's needed
(place new job-levelpermissions:blocks at the very top of the job-specific YAML block) - Standard format: New root-level permissions should be:
permissions: contents: read
- Placement: Insert immediately after the root-level
on:YAML block (no other root-level YAML blocks in between) - Don't reorder existing root-level YAML blocks - only add the permissions block
- Preserve formatting: Match the existing blank line style (see detailed rules below)
🔍 CRITICAL: Blank Line Formatting Rules
STEP 1: Look at the original file. Find the on: root-level YAML block and see what comes immediately after it.
STEP 2: Apply the matching rule:
Rule A - If there is NO blank line between the on: root-level YAML block and the next root-level YAML block:
Insert permissions immediately after the `on:` root-level YAML block with NO blank lines above or below
Rule B - If there IS a blank line between on: root-level YAML block and the next root-level YAML block:
Insert permissions with blank lines both above and below
🧪 MANDATORY: Run This Verification Script
BEFORE making any changes, copy and run this Python script to understand the blank line pattern:
import sys
def analyze_yaml_formatting(file_path):
"""
Analyzes the blank line pattern after the 'on:' block in a YAML workflow file.
Use this to determine which formatting rule (A or B) to apply.
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except Exception as e:
print(f"Error reading file: {e}")
return
# Find the 'on:' root-level block
on_block_end = -1
in_on_block = False
next_block_line = -1
for i, line in enumerate(lines):
stripped = line.strip()
# Found root-level 'on:' block
if stripped == 'on:' and not line.startswith(' '):
in_on_block = True
print(f"Found 'on:' block at line {i+1}")
continue
# We're in the on block, look for the end
if in_on_block:
# If this line has content and is indented, it's part of the on block
if stripped and line.startswith(' '):
on_block_end = i
# If line starts with non-space and isn't empty, we've found the next root-level block
elif stripped and not line.startswith(' '):
next_block_line = i
break
if on_block_end == -1:
print("Could not find complete 'on:' block structure")
return
# Check if there's a blank line between on block and next block
has_blank_line = (on_block_end + 1 < len(lines) and
lines[on_block_end + 1].strip() == '')
print(f"On block ends at line {on_block_end + 1}")
print(f"Next root-level block starts at line {next_block_line + 1}: '{lines[next_block_line].strip()}'")
print(f"Blank line between them: {has_blank_line}")
print()
if has_blank_line:
print("🟢 RULE B APPLIES: Insert permissions with blank lines above and below")
print("Format should be:")
print("on:")
print(" # on block content")
print("")
print("permissions:")
print(" contents: read")
print("")
print("next-block:")
else:
print("🟢 RULE A APPLIES: Insert permissions with NO blank lines")
print("Format should be:")
print("on:")
print(" # on block content")
print("permissions:")
print(" contents: read")
print("next-block:")
# Usage: analyze_yaml_formatting('/path/to/workflow.yml')How to use this script:
- Save the script as
check_formatting.py - Run:
python check_formatting.py(then callanalyze_yaml_formatting('/path/to/workflow.yml')) - Follow the output to determine which rule applies
- Make your changes accordingly
Step 2: Ensure Job-Level Permissions
Check all workflow jobs and add job-specific permissions for any that need more than read permission.
When to Add Job Permissions:
- Steps explicitly using
secrets.GITHUB_TOKENorgithub.token- If the step calls a script, analyze that script to see what permissions are needed
- Steps that use
actions/github-scriptimplicitly usegithub.token- Analyze the script it is executing to see what permissions are needed
- Steps that call a script: Analyze the script to see what permissions are needed
Job Permission Rules:
- Placement: Insert the
permissions:YAML block at the very top of the YAML block for that job, or directly under aneeds:block if one of those exists in the YAML block for that job - Don't reorder existing YAML blocks
- Don't add any new read permissions to job-level permissions
- Preserve existing read permissions if they are already present
- Don't add any new comments, including don't add any trailing line comments
Common Permission Patterns:
JamesIves/github-pages-deploy-action→ needscontents: write- Writing to repository → needs
contents: write - Creating releases → needs
contents: write - Posting comments → needs
issues: writeorpull-requests: write
Step 3: Ensure Job-Level Permissions for Jobs that call another workflow via uses:
After applying Step 2 to all workflows, go back through each workflow job that calls a target workflow via uses:.
These jobs need the permissions from all of the jobs in the target workflow. Apply the same rules from above:
Job Permission Rules:
- Placement: Insert the
permissions:YAML block at the very top of the YAML block for that job, or directly under aneeds:block if one of those exists in the YAML block for that job - Don't reorder existing YAML blocks
- Don't add any new read permissions to job-level permissions
- Preserve existing read permissions if they are already present
- Don't add any new comments, including don't add any trailing line comments
⚠️ Important Exceptions:
- Steps which use other tokens such as
OPENTELEMETRYBOT_GITHUB_TOKEN: Custom tokens don't need workflow permissions - Steps which use
actions/cache/save: Doesn't require special permissions - Don't add unnecessary permissions: Only add what's actually needed
📝 Implementation Guidelines:
- Read each file completely before making changes
- Only modify what's necessary for security compliance
- Maintain existing code style and formatting
- Don't add comments to the workflow files
- No need to test locally (workflows don't run in local builds)