Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions .gitbot-fleet/report-backs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ should match the key in `.gitbot-fleet/enrolment.json` (`rhodibot`, `seambot`, .

The Hypatia verdict grid reads this directory
(`.github/workflows/hypatia-scan-reusable.yml` passes it to
`scripts/verdict-grid.sh --report-backs`), so a report-back written here is the
only way a member's row stops being orange. The envelope is the one in
`scripts/verdict-grid.sh --report-backs`), so a report-back here is the only
way a member's row stops being orange. The envelope is the one in
`docs/VERDICT-GRID-SPEC.adoc` of
[hyperpolymath/standards](https://github.com/hyperpolymath/standards):
[metadatastician/standards](https://github.com/metadatastician/standards):

```json
{ "reporter": "rhodibot",
Expand All @@ -29,12 +29,17 @@ required unless the member ran, and `not_applicable` renders grey. `severity`
uses the fleet's own words (`error`, `warning`, `info`, `suggestion`) or the
scanner words (`critical`, `high`, `medium`, `low`); both normalise.

The directory exists because this repository's deed declares it
(`MetaManifold-WebUI_chora.deed`, `:report-backs ".gitbot-fleet/report-backs/"`)
and a declared path that does not exist is a dangling reference. It is empty
on purpose: no member has reported back yet, and the grid says so - an
enrolled member with no report-back renders **orange** ("enrolled, but no
report-back was received"), never green. Do not add a placeholder envelope to
turn a row green; only a real run may.
**How the files get here.** The `fleet` job of `.github/workflows/hypatia-scan.yml`
(gitbot-fleet's `fleet-report-back-reusable.yml`) runs the enrolled adapters on
the PR head and uploads the envelopes as the `fleet-report-backs` artifact;
the fork-local Hypatia reusable downloads that artifact into this directory
just before it renders the grid (fork delta 4). Nothing is ever committed here:
an envelope is evidence about one commit, and a committed one would read as
current when it is stale. The directory exists because this repository's deed
declares it (`MetaManifold-WebUI_chora.deed`,
`:report-backs ".gitbot-fleet/report-backs/"`), and it is empty in git on
purpose. An enrolled member with no report-back renders **orange** ("enrolled,
but no report-back was received"), never green; do not add a placeholder
envelope to turn a row green. Only a real run may.

Only files matching `*.json` are read, so this README is inert.
38 changes: 37 additions & 1 deletion .github/workflows/hypatia-scan-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# Fork-local copy of metadatastician/standards/.github/workflows/
# hypatia-scan-reusable.yml at 01c9b0c9f2646ebea0b30a9588462642ac94aee9.
# Keep that source pin when updating this file and reapply the three fork deltas:
# Keep that source pin when updating this file and reapply the four fork deltas:
# 1. Do not upload SARIF for pull_request events. This lane is advisory on
# PRs; uploading error-level results creates a separate, blocking
# "Code scanning results / Hypatia" check even when the scan job passes.
Expand All @@ -15,6 +15,12 @@
# 3. Filter only upstream-inherited TypeScript paths from the effective
# findings, validate the raw/effective JSON with pinned ijson-jcs, and keep
# raw scanner outputs plus fork-only paths/rules.
# 4. Download the gitbot-fleet report-backs (artifact `fleet-report-backs`,
# produced by the `fleet` job of hypatia-scan.yml) into
# .gitbot-fleet/report-backs/ before the verdict grid renders, so each
# enrolled member's row carries evidence instead of "enrolled, but no
# report-back was received". Upstream standards added the same step and
# input after 01c9b0c9; this copy carries them ahead of its source pin.
# scripts/check-governance-contract.sh guards these deltas.
#
# Hypatia Neurosymbolic CI/CD Security Scan β€” Reusable Workflow
Expand All @@ -28,6 +34,16 @@ on:
type: boolean
required: false
default: false
report-backs-artifact:
description: >-
Name of an artifact, uploaded earlier in the same run, holding the
gitbot-fleet report-back envelopes (one <member>.json each). Downloaded
into .gitbot-fleet/report-backs/ before the verdict grid renders; a
missing artifact is tolerated and leaves the enrolled rows orange. Set
to an empty string to skip the download.
type: string
required: false
default: "fleet-report-backs"
hypatia-ref:
description: >-
Hypatia commit to build the scanner from. Defaults to a vetted SHA so a
Expand Down Expand Up @@ -728,6 +744,26 @@ jobs:
| last | .body // ""' \
> "$RUNNER_TEMP/grid-previous.md" || : > "$RUNNER_TEMP/grid-previous.md"

# Fork delta 4: fleet report-backs. Produced per run by the `fleet` job of
# hypatia-scan.yml and reaching this job only as an artifact; never
# committed. continue-on-error: no artifact leaves the enrolled rows orange.
# Nothing committed may pose as evidence: only files the artifact supplies
# are read. The committed README.md is not *.json and is left alone.
- name: Clear any checked-out fleet report-backs
if: ${{ always() }}
run: |
set -euo pipefail
mkdir -p .gitbot-fleet/report-backs
find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Delete symlinked report-back entries too

When a PR commits a *.json symlink under this directory, this command skips it because find sees the entry as a symlink rather than -type f. If the artifact download is absent or failsβ€”which is explicitly toleratedβ€”the pinned verdict renderer's *.json loop follows that symlink and accepts valid JSON from its target, allowing committed, fabricated evidence to replace an enrolled member's orange row. Delete every matching directory entry rather than only regular files.

Useful? React with πŸ‘Β / πŸ‘Ž.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed; this PR merged before the finding could land here, so the fix is in the follow-up hyperpolymath/metamanifold-webui#297 (4fbb567): the clear step now removes every directory entry named *.json, symlinks and directories included, and a new mutation that restores the -type f filter is rejected by the contract suite. The same change is prepared for the upstream standards reusable.


Generated by Claude Code


- name: Download the fleet report-backs
if: ${{ always() && inputs.report-backs-artifact != '' }}
continue-on-error: true
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ${{ inputs.report-backs-artifact }}
path: .gitbot-fleet/report-backs
Comment thread
hyperpolymath marked this conversation as resolved.

- name: Render the verdict grid
id: grid
if: ${{ always() }}
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/hypatia-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,24 @@ jobs:
require-files: true
fail-on-indeterminate: true

# Fleet report-backs, produced before the Hypatia lane renders so each enrolled
# gitbot-fleet member's row (.gitbot-fleet/enrolment.json) carries evidence
# instead of "enrolled, but no report-back was received". Same owner
# (hyperpolymath), so the reusable is allowed by this repository's Actions
# policy; scripts/check-governance-contract.sh guards the pin and the wiring.
fleet:
name: Fleet report-backs
needs: [workflow-contract]
uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@e85b71bf139a61f956c7e0717a2912cd6e347cc5
permissions:
contents: read

hypatia:
name: Hypatia verdict grid
needs: [workflow-contract]
# After the fleet job whatever its result (a dead fleet job renders as orange
# fleet rows, never as a missing lane), but never after a failed contract.
needs: [workflow-contract, fleet]
if: ${{ !cancelled() && needs.workflow-contract.result == 'success' }}
# Fork-local so advisory PR scans do not upload error-level SARIF and
# create a second blocking check. The source pin and deltas are documented
# in the called file and checked by scripts/check-governance-contract.sh.
Expand Down
45 changes: 44 additions & 1 deletion scripts/check-governance-contract.sh
100755 β†’ 100644
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,50 @@
not_contains "$caller" 'uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@' \
'the governance lane cannot bypass the fork guard through the pre-transfer name'
contains "$caller" 'needs: [workflow-contract]' \
'this contract runs before the Hypatia scan'
'this contract runs before the fleet job'
contains "$caller" 'needs: [workflow-contract, fleet]' \
'the Hypatia scan runs after both the contract and the fleet report-backs'
contains "$caller" "if: \${{ !cancelled() && needs.workflow-contract.result == 'success' }}" \
'a dead fleet job still lets the Hypatia lane render; a failed contract or a cancelled run does not'
# Fork delta 4: the fleet report-backs. The producer is pinned to a gitbot-fleet
# commit (same owner, so the Actions policy allows the reusable); the consumer
# step must sit before the grid renders, or the rows stay orange with the
# artifact sitting unread in the run.
if grep -Eq '^ uses: hyperpolymath/gitbot-fleet/\.github/workflows/fleet-report-back-reusable\.yml@[0-9a-f]{40}$' <<<"$caller"; then
pass 'the fleet report-backs come from gitbot-fleet at an immutable commit'
else
fail 'the fleet report-backs must come from hyperpolymath/gitbot-fleet pinned to a 40-hex commit'
fi
if fleet_clear="$(step_block "$REUSABLE" 'Clear any checked-out fleet report-backs')"; then
contains "$fleet_clear" "find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete" \
'a committed envelope can never pose as report-back evidence'
clear_line=$(grep -nF -- '- name: Clear any checked-out fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1)
dl_line0=$(grep -nF -- '- name: Download the fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1)
if [ -n "$clear_line" ] && [ -n "$dl_line0" ] && [ "$clear_line" -lt "$dl_line0" ]; then

Check failure on line 148 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7zoLYtQ1PVhg_nmAA&open=AaF7zoLYtQ1PVhg_nmAA&pullRequest=296

Check failure on line 148 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7zoLZtQ1PVhg_nmAC&open=AaF7zoLZtQ1PVhg_nmAC&pullRequest=296

Check failure on line 148 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7zoLZtQ1PVhg_nmAB&open=AaF7zoLZtQ1PVhg_nmAB&pullRequest=296
pass 'the checked-out report-backs are cleared before the artifact is downloaded'
else
fail 'the report-backs clear step must precede the download'
fi
else
fail 'the fleet report-backs clear step exists'
fi
if fleet_dl="$(step_block "$REUSABLE" 'Download the fleet report-backs')"; then
contains "$fleet_dl" 'path: .gitbot-fleet/report-backs' \
'the report-backs land where the renderer reads them'
contains "$fleet_dl" 'continue-on-error: true' \
'a missing report-backs artifact leaves rows orange instead of killing the lane'
contains "$fleet_dl" 'name: ${{ inputs.report-backs-artifact }}' \
'the artifact name is the caller-visible input, not a literal'
dl_line=$(grep -nF -- '- name: Download the fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1)
render_line=$(grep -nF -- '- name: Render the verdict grid' "$REUSABLE" | cut -d: -f1 | head -n1)
if [ -n "$dl_line" ] && [ -n "$render_line" ] && [ "$dl_line" -lt "$render_line" ]; then

Check failure on line 165 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7wMSi78TOH8UMPWHX&open=AaF7wMSi78TOH8UMPWHX&pullRequest=296

Check failure on line 165 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7wMSi78TOH8UMPWHY&open=AaF7wMSi78TOH8UMPWHY&pullRequest=296

Check failure on line 165 in scripts/check-governance-contract.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_MetaManifold-WebUI&issues=AaF7wMSi78TOH8UMPWHW&open=AaF7wMSi78TOH8UMPWHW&pullRequest=296
pass 'the report-backs are downloaded before the verdict grid renders'
else
fail 'the report-backs download must precede the verdict grid render step'
fi
else
fail 'the fleet report-backs download step exists'
fi
if grep -Eq '^ pull_request:$' <<<"$caller"; then
pass 'governance still runs for pull requests'
else
Expand Down
11 changes: 10 additions & 1 deletion scripts/tests/test-governance-contract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ reset_fixture() {
}
reset_fixture
bash "$fixture/scripts/check-governance-contract.sh" > "$fixture/log" 2>&1
for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable; do
for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence fleet-ignores-cancel; do
reset_fixture
case "$mutation" in
allowlist-addition) echo 'frontend/src/fork-only.ts' >> "$fixture/config/hypatia/upstream-typescript-allowlist.txt" ;;
Expand Down Expand Up @@ -49,6 +49,15 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas
deed-remote) sed -i 's|uses: ./.github/workflows/deed-conformance-reusable.yml|uses: metadatastician/standards/.github/workflows/deed-conformance.yml@75bd5994470661a099df562d6d5ad2fe744dbd66|' "$fixture/.github/workflows/hypatia-scan.yml" ;;
scorecard-remote) sed -i 's|uses: ./.github/workflows/scorecard-reusable.yml|uses: metadatastician/standards/.github/workflows/scorecard-reusable.yml@c6d78606f3928c22c3b0bdc6f5f5b47b96e05a5d|' "$fixture/.github/workflows/scorecard.yml" ;;
cross-owner-reusable) sed -i 's|uses: ./.github/workflows/k9-conformance-reusable.yml|uses: someone/else/.github/workflows/k9.yml@main|' "$fixture/.github/workflows/hypatia-scan.yml" ;;
# Fork delta 4: the fleet report-backs must come from an immutable gitbot-fleet
# commit, be downloaded before the grid renders, and never kill the lane.
fleet-unpinned) sed -i -E 's|(fleet-report-back-reusable\.yml)@[0-9a-f]{40}|\1@main|' "$fixture/.github/workflows/hypatia-scan.yml" ;;
fleet-before-contract) sed -i 's|needs: \[workflow-contract, fleet\]|needs: [fleet]|' "$fixture/.github/workflows/hypatia-scan.yml" ;;
fleet-dropped-download) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;;
fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;;
fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;;
fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-print -delete$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;;
fleet-ignores-cancel) sed -i "s|if: \${{ !cancelled() \&\& needs.workflow-contract.result == 'success' }}|if: \${{ always() \&\& needs.workflow-contract.result == 'success' }}|" "$fixture/.github/workflows/hypatia-scan.yml" ;;
# A copy is only a copy while it still comes from the audited commit and
# keeps the guards whose absence froze code scanning upstream.
deed-source-sha) sed -i 's@75bd5994470661a099df562d6d5ad2fe744dbd66@main@' "$fixture/.github/workflows/deed-conformance-reusable.yml" ;;
Expand Down
Loading