Skip to content

Add minimum token permissions for all github workflow files #6

@opentelemetrybot

Description

@opentelemetrybot

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:

  1. 🔍 READ THE FILE FIRST: Always examine the existing formatting style before making any changes
  2. Root-Level permissions MUST be limited to either read-all or contents: read
  3. If the existing rootl-level permission is read-all then leave it unchanged
  4. 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-level permissions: blocks at the very top of the job-specific YAML block)
  5. Standard format: New root-level permissions should be:
    permissions:
      contents: read
  6. Placement: Insert immediately after the root-level on: YAML block (no other root-level YAML blocks in between)
  7. Don't reorder existing root-level YAML blocks - only add the permissions block
  8. 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:

  1. Save the script as check_formatting.py
  2. Run: python check_formatting.py (then call analyze_yaml_formatting('/path/to/workflow.yml'))
  3. Follow the output to determine which rule applies
  4. 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_TOKEN or github.token
    • If the step calls a script, analyze that script to see what permissions are needed
  • Steps that use actions/github-script implicitly use github.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:

  1. Placement: Insert the permissions: YAML block at the very top of the YAML block for that job, or directly under a needs: block if one of those exists in the YAML block for that job
  2. Don't reorder existing YAML blocks
  3. Don't add any new read permissions to job-level permissions
  4. Preserve existing read permissions if they are already present
  5. Don't add any new comments, including don't add any trailing line comments

Common Permission Patterns:

  • JamesIves/github-pages-deploy-action → needs contents: write
  • Writing to repository → needs contents: write
  • Creating releases → needs contents: write
  • Posting comments → needs issues: write or pull-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:

  1. Placement: Insert the permissions: YAML block at the very top of the YAML block for that job, or directly under a needs: block if one of those exists in the YAML block for that job
  2. Don't reorder existing YAML blocks
  3. Don't add any new read permissions to job-level permissions
  4. Preserve existing read permissions if they are already present
  5. 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)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions