-
-
Notifications
You must be signed in to change notification settings - Fork 0
fix(ci): secure scanner and mirror provenance; reconcile impossible gates #764
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
73856a8
d746d46
0ec476f
953756f
8e50188
5ab3a28
af24e41
460c375
62e38a7
febccbd
874ffe5
e9fa12b
591cea1
da2c748
c78f914
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,16 +45,36 @@ jobs: | |
| # estate froze: measured in this repo on 2026-08-25, all 12 Scorecard | ||
| # alerts were stuck at 2026-06-03 while Hypatia's updated 2026-08-24. | ||
| # The badge kept working, which is why it went unnoticed. | ||
| - name: Checkout native-lock reconciliation helper | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| repository: hyperpolymath/standards | ||
| ref: 591cea1b074b56367f25ec0084a0c4ab66b42a7e | ||
| path: .standards-scorecard | ||
| persist-credentials: false | ||
| sparse-checkout: scripts/reconcile-scorecard-actions-lock.rb | ||
| sparse-checkout-cone-mode: false | ||
| - name: Reconcile native action pins | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| gh extension install github/gh-actions-lock --pin v0.1.6 | ||
| ruby .standards-scorecard/scripts/reconcile-scorecard-actions-lock.rb \ | ||
| results.sarif results.reconciled.sarif actions-lock-audit.json | ||
| - name: Upload SARIF to code scanning | ||
| uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v3 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Change the annotation to The repository contract requires each immutable action pin to identify its exact release. This SHA resolves to CodeQL Action 🤖 Prompt for AI Agents |
||
| with: | ||
| sarif_file: results.sarif | ||
| sarif_file: results.reconciled.sarif | ||
|
|
||
| - name: Upload results artifact | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: scorecard-results | ||
| path: results.sarif | ||
| path: | | ||
| results.sarif | ||
| results.reconciled.sarif | ||
| actions-lock-audit.json | ||
| retention-days: 90 | ||
|
|
||
| pull-request: | ||
|
|
@@ -126,14 +146,34 @@ jobs: | |
| jq -r '.runs[].automationDetails.id' results.sarif | ||
| n=$(jq '[.runs[].automationDetails.id | split("/")[0:2] | join("/")] | unique | length' results.sarif) | ||
| test "$n" -eq 3 || { echo "expected 3 Scorecard categories, got $n" >&2; exit 1; } | ||
| - name: Checkout native-lock reconciliation helper | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| repository: hyperpolymath/standards | ||
| ref: 591cea1b074b56367f25ec0084a0c4ab66b42a7e | ||
| path: .standards-scorecard | ||
| persist-credentials: false | ||
| sparse-checkout: scripts/reconcile-scorecard-actions-lock.rb | ||
| sparse-checkout-cone-mode: false | ||
| - name: Reconcile native action pins | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| gh extension install github/gh-actions-lock --pin v0.1.6 | ||
| ruby .standards-scorecard/scripts/reconcile-scorecard-actions-lock.rb \ | ||
| results.sarif results.reconciled.sarif actions-lock-audit.json | ||
| - name: Upload SARIF to code scanning | ||
| uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 | ||
| with: | ||
| sarif_file: results.sarif | ||
| sarif_file: results.reconciled.sarif | ||
| - name: Retain scan evidence | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: scorecard-pr-results | ||
| path: results.sarif | ||
| path: | | ||
| results.sarif | ||
| results.reconciled.sarif | ||
| actions-lock-audit.json | ||
| if-no-files-found: error | ||
| retention-days: 14 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # A CI policy must not require files that the structural-drift gate forbids. | ||
| set -euo pipefail | ||
| status=0 | ||
| while IFS= read -r -d '' file; do | ||
| [[ -f "$file" ]] || continue | ||
| # Restrict this check to executable file-existence tests. Historical prose | ||
| # and commented examples are not policy enforcement. | ||
| if awk ' | ||
| /^[[:space:]]*#/ { next } | ||
| { | ||
| source=$0 | ||
| # Mask quoted prose while retaining literal path arguments. Keep command | ||
| # substitutions visible: an echo can still execute a file test in $(). | ||
| code=""; quote=""; quoted="" | ||
| for (i=1; i<=length(source); i++) { | ||
| ch=substr(source,i,1) | ||
| if (quote != "") { | ||
| if (ch == quote) { | ||
| if (quoted ~ /^\.machine_readable\/(6a2\/)?(STATE|META|ECOSYSTEM|AGENTIC|NEUROSYM|PLAYBOOK|ANCHOR)\.a2ml$/ || | ||
| (quote == "\"" && quoted ~ /\$\(|`/)) code=code quoted | ||
| else code=code " " | ||
|
Comment on lines
+21
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Detect retired paths split across shell quoting. The scanner misses 🤖 Prompt for AI Agents
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you complete this as recommended rabbit? |
||
| quote=""; quoted="" | ||
| } else if (ch == "\\" && quote == "\"") { | ||
| quoted=quoted ch substr(source,++i,1) | ||
| } else quoted=quoted ch | ||
| } else if (ch == "\"" || ch == sprintf("%c",39)) quote=ch | ||
| else code=code ch | ||
| } | ||
| # A multiline shell quote cannot be classified from this physical line. | ||
| if (quote != "") code=code quoted | ||
| if (code ~ /(-f[[:space:]]|-e[[:space:]]|check_file[[:space:]])/ && | ||
| code ~ /\.machine_readable\/(6a2\/)?(STATE|META|ECOSYSTEM|AGENTIC|NEUROSYM|PLAYBOOK|ANCHOR)\.a2ml/) { | ||
| found=1; print FNR ":" source | ||
| } | ||
| } | ||
| END { exit !found } | ||
| ' "$file"; then | ||
| printf '::error file=%s::Policy requires a retired descriptile path; use .machine_readable/descriptiles/ and reconcile existing files\n' "$file" | ||
| status=1 | ||
| fi | ||
| done < <(git ls-files -z -- '.github/workflows/*.yml' '.github/workflows/*.yaml' 'scripts/*.sh' '.githooks/*.sh' Justfile justfile) | ||
| exit "$status" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #!/usr/bin/env ruby | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # Produce a reviewable PUT body for the four retired constraints documented | ||
| # in config/README.adoc. This script never calls GitHub or changes a ruleset. | ||
| require 'json' | ||
|
|
||
| module RulesetConstraintRepair | ||
| RETIRED = %w[update required_deployments code_quality code_coverage].freeze | ||
| WRITABLE = %w[name target enforcement conditions bypass_actors rules].freeze | ||
|
|
||
| # Supply an app ID only after GitHub rejects it as no longer installed. Removing | ||
| # a stale bypass tightens access; never infer app availability from its name. | ||
| def self.plan(source, uninstalled_app_id: nil) | ||
| unless source.is_a?(Hash) && source['target'] == 'branch' && source['enforcement'] == 'active' && | ||
| source.dig('conditions', 'ref_name', 'include') == ['~DEFAULT_BRANCH'] && | ||
| source.dig('conditions', 'ref_name', 'exclude') == [] && source['rules'].is_a?(Array) | ||
| raise ArgumentError, 'Expected an active default-branch ruleset with no exclusions' | ||
| end | ||
| rules = source.fetch('rules') | ||
| unless rules.all? { |rule| rule.is_a?(Hash) && rule['type'].is_a?(String) } | ||
| raise ArgumentError, 'Malformed rule data; refusing a partial plan' | ||
| end | ||
| raise ArgumentError, 'Missing pull-request protection' unless rules.any? { |r| r['type'] == 'pull_request' } | ||
|
|
||
| result = source.select { |key, _value| WRITABLE.include?(key) } | ||
| result['rules'] = rules.reject { |rule| RETIRED.include?(rule['type']) } | ||
| if uninstalled_app_id | ||
| actors = source.fetch('bypass_actors') | ||
| unless uninstalled_app_id.is_a?(Integer) && uninstalled_app_id.positive? && | ||
| actors.is_a?(Array) && actors.any? { |actor| actor['actor_type'] == 'Integration' && actor['actor_id'] == uninstalled_app_id } | ||
| raise ArgumentError, 'Expected the exact ID of an existing integration bypass rejected by GitHub' | ||
| end | ||
| result['bypass_actors'] = actors.reject { |actor| actor['actor_type'] == 'Integration' && actor['actor_id'] == uninstalled_app_id } | ||
| end | ||
| result | ||
| end | ||
| end | ||
|
|
||
| if $PROGRAM_NAME == __FILE__ | ||
| unless ARGV.length == 1 || (ARGV.length == 3 && ARGV[1] == '--remove-uninstalled-app') | ||
| abort 'Usage: plan-ruleset-constraint-repair.rb <live-ruleset.json> [--remove-uninstalled-app <verified-id>]' | ||
| end | ||
| app_id = ARGV[2] && Integer(ARGV[2], 10) | ||
| puts JSON.pretty_generate(RulesetConstraintRepair.plan(JSON.parse(File.read(ARGV[0])), uninstalled_app_id: app_id)) | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.