-
Notifications
You must be signed in to change notification settings - Fork 0
fix(scheduler): require approved aggregate review state #937
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
Closed
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
6749c78
fix(scheduler): require approved aggregate review state
dd8f59d
test(scheduler): cover missing aggregate review state
12e3d1f
test(scheduler): cover empty aggregate review state
2756cd3
fix(scheduler): block merge on running checks
5856151
fix(strix): fail closed on missing evidence
9a05f03
fix(scheduler): bind check evidence to current head
170b98e
fix(ci): make review model pool portable
ed666c7
test(security): pin Strix dependency floors
7750253
fix(ci): close coverage and Strix smoke gaps
249ba98
fix(ci): close scheduler evidence gaps
ab2a1ae
fix(ci): require trustworthy Strix evidence
seonghobae e6c6d12
fix(ci): bind scan reports and kill captured groups
seonghobae 2c6f432
fix(ci): reject conflicting scan metadata
seonghobae 8726df1
fix(ci): verify PR Strix workflow version
seonghobae 67d834f
fix(ci): validate executable Strix workflow policy
seonghobae 19ced89
fix(ci): bound review dispatch payloads and UX failures
seonghobae d6b9b3a
fix(ci): defer interpreter-incompatible lock candidates
seonghobae 2fe2bba
fix(ci): retry transient trusted uv downloads
seonghobae 9644f9f
docs(adr): record dispatch allowlist drift
seonghobae 0cc98ac
docs(adr): record stale dependency alerts
seonghobae 5aafbb2
docs(adr): record trusted Strix outage evidence
seonghobae 65d4b08
test(ci): align Strix lock contract wording
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| name: Strix Workflow Contract | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, ready_for_review] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| workflow-contract: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Read PR Strix workflow as data | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} | ||
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||
| run: | | ||
| set -euo pipefail | ||
| if ! [[ "$HEAD_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || | ||
| ! [[ "$HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then | ||
| echo "::error::PR workflow contract metadata is malformed." | ||
| exit 1 | ||
| fi | ||
| workflow_json="$(gh api "repos/${HEAD_REPOSITORY}/contents/.github/workflows/strix.yml?ref=${HEAD_SHA}")" | ||
| printf '%s' "$workflow_json" | | ||
| jq -r '.content // empty' | | ||
| tr -d '\n' | | ||
| base64 --decode > "$RUNNER_TEMP/strix-pr-workflow.yml" | ||
| workflow_file="$RUNNER_TEMP/strix-pr-workflow.yml" | ||
| test -s "$workflow_file" | ||
| ruby - "$workflow_file" <<'RUBY' | ||
| require "psych" | ||
|
|
||
| def reject!(message) | ||
| warn "::error::PR Strix workflow contract rejected: #{message}" | ||
| exit 1 | ||
| end | ||
|
|
||
| def executable_source(run) | ||
| run.lines.reject { |line| line.lstrip.start_with?("#") }.join | ||
| end | ||
|
|
||
| def statically_reachable?(node) | ||
| condition = node["if"] | ||
| return true if condition.nil? | ||
| return false if condition == false | ||
|
|
||
| value = condition.to_s.strip | ||
| !value.match?(/\b(?:false|0\s*==\s*1|1\s*==\s*0)\b/i) | ||
| end | ||
|
|
||
| workflow_path = ARGV.fetch(0) | ||
| begin | ||
| workflow = Psych.safe_load(File.read(workflow_path), aliases: false) | ||
| rescue Psych::Exception => error | ||
| reject!("workflow is not valid YAML: #{error.message.lines.first.strip}") | ||
| end | ||
| reject!("top-level YAML value is not a mapping") unless workflow.is_a?(Hash) | ||
|
|
||
| jobs = workflow["jobs"] | ||
| strix = jobs.is_a?(Hash) && jobs["strix"] | ||
| steps = strix.is_a?(Hash) && strix["steps"] | ||
| reject!("jobs.strix.steps is not a sequence") unless steps.is_a?(Array) | ||
| reject!("jobs.strix is statically unreachable") unless statically_reachable?(strix) | ||
|
|
||
| reachable_steps = steps.select do |step| | ||
| step.is_a?(Hash) && statically_reachable?(step) | ||
| end | ||
| named_step = lambda do |name| | ||
| reachable_steps.find { |step| step["name"] == name } | ||
| end | ||
| run_source = lambda do |step| | ||
| step && step["run"].is_a?(String) ? executable_source(step["run"]) : "" | ||
| end | ||
|
|
||
| gate_step = reachable_steps.find do |step| | ||
| source = run_source.call(step) | ||
| source.include?("if [ \"$strix_rc\" -eq 0 ]; then") && | ||
| source.include?("echo \"::error title=Strix evidence incomplete::") && | ||
| source.match?(/exit\s+[\"']?\$strix_rc/) | ||
| end | ||
| reject!("fail-closed gate is missing from a reachable run step") unless gate_step | ||
|
|
||
| collect_step = named_step.call("Collect Strix reports for artifact upload") | ||
| validate_step = named_step.call("Validate Strix report provenance") | ||
| upload_step = named_step.call("Upload Strix reports artifact") | ||
| reject!("structured report collection step is missing or unreachable") unless collect_step | ||
| reject!("structured provenance validation step is missing or unreachable") unless validate_step | ||
| reject!("report upload step is missing or unreachable") unless upload_step | ||
|
|
||
| gate_index = reachable_steps.index(gate_step) | ||
| collect_index = reachable_steps.index(collect_step) | ||
| validate_index = reachable_steps.index(validate_step) | ||
| upload_index = reachable_steps.index(upload_step) | ||
| unless gate_index < collect_index && collect_index < validate_index && validate_index < upload_index | ||
| reject!("fail-closed gate, collection, provenance validation, and upload are out of order") | ||
| end | ||
|
|
||
| validation_source = run_source.call(validate_step) | ||
| required_fragments = { | ||
| "scan-stage head binding" => "scan_stage_head_sha", | ||
| "candidate metadata conflict rejection" => "if [ \"$candidate_metadata_matches\" -ne 1 ]; then", | ||
| "completed successful run selection" => "if ! jq -e '(.status == \"completed\") and (.scan_results.scan_completed == true) and (.scan_results.success == true)'", | ||
| "non-empty report requirement" => "if [ -s \"$candidate_report\" ]; then", | ||
| "artifact evidence binding" => "> \"$GITHUB_WORKSPACE/strix_runs/evidence-binding.json\"", | ||
| "report digest" => "report_sha256=\"$(sha256sum \"$report_file\" | awk '{print $1}')\"", | ||
| "completed binding flag" => "{head_sha:$head_sha, run_id:$run_id, run_json:$run_json, report:$report, report_sha256:$report_sha256, scan_completed:true}", | ||
| "provider fail-closed guard" => "if [ -f \"$gate_console\" ] && grep -Eiq", | ||
| "candidate report path" => "candidate_report=\"$(dirname -- \"$candidate_run\")/penetration_test_report.md\"" | ||
| } | ||
| required_fragments.each do |label, fragment| | ||
| reject!("#{label} is absent from executable provenance validation") unless validation_source.include?(fragment) | ||
| end | ||
|
|
||
| neutral_marker = "neutral skip so an infrastructure outage does not block merges" | ||
| reject!("Strix workflow still neutralizes missing security evidence") if executable_source(File.read(workflow_path)).include?(neutral_marker) | ||
| RUBY |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.