From bb0016789698b17930272a747a5376919304bb72 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:35:32 +0000 Subject: [PATCH 1/3] fix(compliance-audit): eliminate false positives in three checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dependabot YAML quote style: grep patterns now accept both single-quoted ('github-actions') and double-quoted ("github-actions") YAML values. YAML allows either; the old patterns required double quotes, flagging valid single-quoted configs as non-compliant. - Workflow permissions: reusable workflows (workflow_call-only triggers) are now skipped. Their permissions are set by the caller, so requiring a top-level permissions: block in *-reusable.yml files was a false positive that blocked every org-level reusable workflow. - AGENTS.md org reference: accept GitHub blob URLs (petry-projects/.github/blob//AGENTS.md) in addition to the canonical path format (.github/AGENTS.md in link text). Both forms unambiguously point to the org-level standards file. Closes #119 (partial — API-based fixes applied separately) Co-authored-by: don-petry --- scripts/compliance-audit.sh | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/scripts/compliance-audit.sh b/scripts/compliance-audit.sh index 3c1d2a8fa..2473eb732 100755 --- a/scripts/compliance-audit.sh +++ b/scripts/compliance-audit.sh @@ -238,20 +238,22 @@ check_dependabot_config() { decoded=$(echo "$content" | base64 -d 2>/dev/null || echo "") # Check github-actions ecosystem entry exists - if ! echo "$decoded" | grep -q 'package-ecosystem:.*"github-actions"'; then + # Accept both double-quoted ("github-actions") and single-quoted ('github-actions') YAML values. + if ! echo "$decoded" | grep -qE "package-ecosystem:[[:space:]]*(\"github-actions\"|'github-actions')"; then add_finding "$repo" "dependabot" "missing-github-actions-ecosystem" "error" \ "Dependabot config missing \`github-actions\` ecosystem entry" \ "standards/dependabot-policy.md#github-actions-all-repos" fi # Check that app ecosystem entries use open-pull-requests-limit: 0 - # Extract ecosystem blocks and check limits + # Extract ecosystem blocks and check limits. + # Accept both double-quoted and single-quoted YAML string values. for eco in npm pip gomod cargo terraform; do - if echo "$decoded" | grep -q "package-ecosystem:.*\"$eco\""; then + if echo "$decoded" | grep -qE "package-ecosystem:[[:space:]]*(\"$eco\"|'$eco')"; then # Check if this ecosystem has limit: 0 # Simple heuristic: find the ecosystem line and look for limit in the next ~10 lines local block - block=$(echo "$decoded" | awk "/package-ecosystem:.*\"$eco\"/{found=1} found{print; if(/package-ecosystem:/ && NR>1 && !/\"$eco\"/) exit}" | head -15) + block=$(echo "$decoded" | awk "/package-ecosystem:.*$eco/{found=1} found{print; if(/package-ecosystem:/ && NR>1 && !/$eco/) exit}" | head -15) local limit limit=$(echo "$block" | grep 'open-pull-requests-limit:' | head -1 | grep -oE '[0-9]+' || echo "") if [ -n "$limit" ] && [ "$limit" != "0" ]; then @@ -262,13 +264,14 @@ check_dependabot_config() { fi done - # Check for required labels in dependabot config - if ! echo "$decoded" | grep -q '"security"'; then + # Check for required labels in dependabot config. + # Accept both double-quoted and single-quoted YAML string values. + if ! echo "$decoded" | grep -qE '("security"|'"'"'security'"'"')'; then add_finding "$repo" "dependabot" "missing-security-label" "warning" \ "Dependabot config missing \`security\` label on updates" \ "standards/dependabot-policy.md#policy" fi - if ! echo "$decoded" | grep -q '"dependencies"'; then + if ! echo "$decoded" | grep -qE '("dependencies"|'"'"'dependencies'"'"')'; then add_finding "$repo" "dependabot" "missing-dependencies-label" "warning" \ "Dependabot config missing \`dependencies\` label on updates" \ "standards/dependabot-policy.md#policy" @@ -479,6 +482,17 @@ check_workflow_permissions() { decoded=$(echo "$content" | base64 -d 2>/dev/null || echo "") [ -z "$decoded" ] && continue + # Skip reusable workflows (workflow_call-only triggers). + # Their permissions are controlled entirely by the caller workflow, so + # requiring a top-level permissions: block here would be redundant and + # would generate false positives for every *-reusable.yml in the org. + local on_triggers + on_triggers=$(echo "$decoded" | awk '/^on:/{found=1; next} found && /^[^ ]/{exit} found{print}') + if echo "$on_triggers" | grep -q 'workflow_call:' && \ + ! echo "$on_triggers" | grep -qE '^\s+(push|pull_request|schedule|workflow_dispatch|issues|issue_comment|pull_request_target):'; then + continue + fi + # Check if the workflow has a top-level permissions key # Single-job workflows may define permissions at job level instead if ! echo "$decoded" | grep -qE '^permissions:'; then @@ -776,7 +790,11 @@ check_agents_md() { local decoded decoded=$(echo "$content" | base64 -d 2>/dev/null || echo "") - if ! echo "$decoded" | grep -qE '\.github/AGENTS\.md'; then + # Accept two forms of reference: + # 1. Canonical path format: petry-projects/.github/AGENTS.md (appears in link text) + # 2. GitHub blob URL format: /petry-projects/.github/blob//AGENTS.md (in href) + # Both unambiguously point to the org-level standards file. + if ! echo "$decoded" | grep -qE '(\.github/AGENTS\.md|petry-projects/\.github/blob/[^/]+/AGENTS\.md)'; then add_finding "$repo" "standards" "agents-md-missing-org-ref" "error" \ "\`AGENTS.md\` does not reference the org-level \`.github/AGENTS.md\` standards" \ "AGENTS.md" From 4b906a531f604540b74971c0e2fa0d7d565abbc5 Mon Sep 17 00:00:00 2001 From: DJ Date: Thu, 9 Apr 2026 18:02:36 -0700 Subject: [PATCH 2/3] fix(compliance-audit): address CodeRabbit + Copilot review feedback - Replace brittle awk/grep reusable-workflow detection with filename convention check (*-reusable.yml), avoiding false skips from unlisted triggers or inline on: syntax - Fix blob URL regex to allow slashed branch names (blob/.+/AGENTS.md instead of blob/[^/]+/AGENTS.md) - Align comment with what the regex actually matches Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/compliance-audit.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/compliance-audit.sh b/scripts/compliance-audit.sh index 2473eb732..696fb61d3 100755 --- a/scripts/compliance-audit.sh +++ b/scripts/compliance-audit.sh @@ -486,10 +486,8 @@ check_workflow_permissions() { # Their permissions are controlled entirely by the caller workflow, so # requiring a top-level permissions: block here would be redundant and # would generate false positives for every *-reusable.yml in the org. - local on_triggers - on_triggers=$(echo "$decoded" | awk '/^on:/{found=1; next} found && /^[^ ]/{exit} found{print}') - if echo "$on_triggers" | grep -q 'workflow_call:' && \ - ! echo "$on_triggers" | grep -qE '^\s+(push|pull_request|schedule|workflow_dispatch|issues|issue_comment|pull_request_target):'; then + # All reusable workflows follow the -reusable.yml naming convention. + if [[ "$wf" == *-reusable.yml || "$wf" == *-reusable.yaml ]]; then continue fi @@ -791,10 +789,10 @@ check_agents_md() { decoded=$(echo "$content" | base64 -d 2>/dev/null || echo "") # Accept two forms of reference: - # 1. Canonical path format: petry-projects/.github/AGENTS.md (appears in link text) - # 2. GitHub blob URL format: /petry-projects/.github/blob//AGENTS.md (in href) - # Both unambiguously point to the org-level standards file. - if ! echo "$decoded" | grep -qE '(\.github/AGENTS\.md|petry-projects/\.github/blob/[^/]+/AGENTS\.md)'; then + # 1. Any path containing .github/AGENTS.md (relative link text or path reference) + # 2. GitHub blob URL format: /petry-projects/.github/blob//AGENTS.md (in href) + # Both are treated as references to the org-level standards file. + if ! echo "$decoded" | grep -qE '(\.github/AGENTS\.md|petry-projects/\.github/blob/.+/AGENTS\.md)'; then add_finding "$repo" "standards" "agents-md-missing-org-ref" "error" \ "\`AGENTS.md\` does not reference the org-level \`.github/AGENTS.md\` standards" \ "AGENTS.md" From 9d5c4e7689e9c91b3cddd0ba1465dcbd746ca0ee Mon Sep 17 00:00:00 2001 From: DJ Date: Thu, 9 Apr 2026 18:08:16 -0700 Subject: [PATCH 3/3] fix(compliance-audit): quote-aware awk pattern for ecosystem block extraction The awk pattern used unquoted $eco which caused "npm" to substring-match "pnpm", potentially spanning the extracted block into the wrong ecosystem entry. Align awk with the existing grep by requiring quoted values. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/compliance-audit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/compliance-audit.sh b/scripts/compliance-audit.sh index 696fb61d3..8550bf3fb 100755 --- a/scripts/compliance-audit.sh +++ b/scripts/compliance-audit.sh @@ -253,7 +253,7 @@ check_dependabot_config() { # Check if this ecosystem has limit: 0 # Simple heuristic: find the ecosystem line and look for limit in the next ~10 lines local block - block=$(echo "$decoded" | awk "/package-ecosystem:.*$eco/{found=1} found{print; if(/package-ecosystem:/ && NR>1 && !/$eco/) exit}" | head -15) + block=$(echo "$decoded" | awk "/package-ecosystem:.*(\"$eco\"|'$eco')/{found=1} found{print; if(/package-ecosystem:/ && NR>1 && !/(\"$eco\"|'$eco')/) exit}" | head -15) local limit limit=$(echo "$block" | grep 'open-pull-requests-limit:' | head -1 | grep -oE '[0-9]+' || echo "") if [ -n "$limit" ] && [ "$limit" != "0" ]; then