Repository navigation
Conversation
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Superseded — see the current review report for commit
|
| run_id="$(gh run list --repo "$GITHUB_REPOSITORY" --workflow rekor-monitor.yaml \ | ||
| --branch main --status completed --limit 1 --json databaseId --jq '.[0].databaseId // empty')" | ||
| if [ -n "$run_id" ]; then | ||
| gh run download "$run_id" --repo "$GITHUB_REPOSITORY" \ | ||
| --name rekor-monitor-state --dir monitor-state |
There was a problem hiding this comment.
🟠 Bug (high confidence): --status completed also matches cancelled, skipped and failed runs, including runs that never uploaded rekor-monitor-state. That happens if a run fails before monitor-state/ has files (for example in ca-trust-guard, locked-tools, or a transient gh run download error, where if-no-files-found: error drops the upload), or if a queued run is cancelled by the concurrency group. The next run then picks that run, gh run download fails, and it uploads nothing either, so every later run fails the same way. Select the most recent run that actually has the artifact instead, for example gh api repos/$REPO/actions/artifacts?name=rekor-monitor-state filtered to workflow_run.head_branch == "main" and not expired.
| retry = {row["uuid"] for row in rows if row["status"] in {"PENDING", "MONITOR-ERROR"} | ||
| or (row["status"] == "UNKNOWN" and "unavailable" in row["reason"] | ||
| and now - state.get("first_seen", {}).get(row["uuid"], now) < WINDOW)} | ||
| state["pending"] = [item for uuid, item in observations.items() if uuid in retry] | ||
| state["first_seen"] = {key: value for key, value in state.get("first_seen", {}).items() if key in retry} |
There was a problem hiding this comment.
🟡 Suggestion (medium confidence): MONITOR-ERROR rows are retried with no window, unlike PENDING/UNKNOWN. A deterministic helper error, such as a watched-identity hashedrekord using sha512 (unsupported signed digest) or certificate identity differs from observation, will be re-fetched and fail the job every hour. It can only be cleared by hand-editing the artifact state. Consider alerting once and then expiring it, or recording it as a terminal anomaly, after WINDOW.
Superseded — see the current review report for commit
|
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Superseded — see the current review report for commit
|
| while IFS= read -r candidate; do | ||
| [ -n "$candidate" ] || continue | ||
| candidate_workflow="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$candidate" --jq '.workflow_id')" | ||
| if [ "$candidate_workflow" = "$workflow_id" ]; then | ||
| run_id="$candidate" | ||
| break | ||
| fi | ||
| done <<< "$candidates" | ||
| if [ -n "$run_id" ]; then | ||
| gh run download "$run_id" --repo "$GITHUB_REPOSITORY" \ | ||
| --name rekor-monitor-state --dir monitor-state | ||
| test -s monitor-state/checkpoint.txt | ||
| test -s monitor-state/reconciliation.json |
There was a problem hiding this comment.
🟡 Suggestion (medium confidence): this picks the newest artifact, but nothing checks that its contents are complete before it wins. A failed bootstrap can upload an artifact with only diagnostics.log (rekor_monitor failed) or with no reconciliation.json (Python failed), because the upload step runs on always(). Every later run downloads it, fails test -s, and uploads the same incomplete files again, so the monitor stays stuck until someone deletes the artifact by hand. Either download each candidate into a temp dir and fall back to the next one when either file is missing, or skip the upload unless both files exist.
Superseded — see the current review report for commit
|
Superseded by a completed review with no blocking issues; current audit evidence persisted in CI.
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Superseded — see the current review report for commit
|
| if gh run download "$candidate" --repo "$GITHUB_REPOSITORY" \ | ||
| --name rekor-monitor-state --dir "$candidate_dir" && | ||
| test -s "$candidate_dir/checkpoint.txt" && | ||
| test -s "$candidate_dir/reconciliation.json"; then | ||
| rmdir monitor-state | ||
| mv "$candidate_dir" monitor-state | ||
| candidate_dir="" | ||
| run_id="$candidate" | ||
| break | ||
| fi | ||
| rm -rf "$candidate_dir" | ||
| candidate_dir="" |
There was a problem hiding this comment.
🟡 Suggestion (medium confidence): a temporary gh run download failure on the newest artifact now silently falls back to an older one with no log line. That restores an older reconciliation.json (pending entries, first_seen, digest budget, checkpoint first_observed), and that older state is then uploaded as the newest. Consider emitting ::warning:: with the skipped run ID, and failing outright when the download errors, as opposed to when it succeeds but the files are incomplete.
Superseded — see the current review report for commit
|
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Superseded — see the current review report for commit
|
| retry = {row["uuid"] for row in rows if row["status"] in {"PENDING", "MONITOR-ERROR"} | ||
| or (row["status"] == "UNKNOWN" and "unavailable" in row["reason"] | ||
| and now - state.get("first_seen", {}).get(row["uuid"], now) < WINDOW)} |
There was a problem hiding this comment.
🟡 Suggestion: MONITOR-ERROR rows go into retry without any WINDOW limit. evaluate() also returns before first_seen is set for them (line 135-136), so the age check could not apply even if it were added. If one entry keeps failing the helper the same way, it stays in pending forever and the job fails every hour. Consider recording first_seen for error rows and expiring them after WINDOW (or escalating them to a terminal ANOMALY).
General PR Review: Monitor release identities and published signed digestsBlocking Issues: 0 | Suggestions: 1 | Threads Resolved: 0 Review SummaryThe new commit makes state restore fail closed. If Security IssuesNone found. Correctness IssuesNone found. Suggestions
Resolved prior findings
Prompt for AI agentsReviewed commit: |
Why
Watch new signatures under the three release-workflow identities continuously, and distinguish a real workflow run from authorization of the bytes that were signed.
What
rekor_monitordirectly at an exact Go pseudo-version; the reusable workflow exports only a checkpoint artifact, not matched entries. Use the actual YAML output keys (outputIdentities,outputIdentitiesFormat) and the explicit v1 endpoint.UNAUTHENTICATED-SANdiagnostics, never identity matches.Bounded UNKNOWN semantics
DSSE/intoto payloads, hidden catalogs (including HTTP 403), unknown storage aliases and missing/non-joinable extensions stay UNKNOWN. A private run that is inaccessible with this repository token also stays UNKNOWN; no access expansion is attempted.
Hashedrekord OCI signatures contain a simple-signing-payload hash, not the image digest. On an image-capable release, at most N distinct unrecorded hashes remain UNKNOWN after the six-hour window; N is the count of public-ECR image indexes (
isIndex: true). Private Lambda images do not grant budget. The next distinct unrecorded hash is an anomaly. The manifest has no explicitsignedflag today; this predicate encodes the current signed-image shape and must be revisited if that shape/schema changes. Artifact-only releases have zero budget, so a valid-run/unrecorded digest is an anomaly. The budget persists across runs.Tool and trust-material provenance
47017b2379cb3452271ce0cc1e33d05f61c5e275.github.com/sigstore/rekor-monitor v0.0.0-20261019132539-47017b2379cb.h1:+ICdEtSt8tWgvUR8YdwoMOavuNf2yWEqH5FXSpxVdhI=; go.mod sum:h1:b8wU/k2HXf4mHROlRsUh/Mdd5+7bfSosG67J4GSRHLw=. Installation usesGOTOOLCHAIN=local,GOFLAGS=-mod=readonly,GOSUMDB=sum.golang.org; the helper has its own checked-in dependency sums.6494e21ea73fa7ee769f85f57d5a3e6a08725eae1e38c755fc3517c9e6bc0b66.d543eb6e067c069c6b2fd2c22258452db2f923b221597303a6bce4c664209ee6.ac5a60ca80f473f8b7742111f9eb49bf7dbd8f03f41a5b39d58b62b6f0de4d0f.Evidence
make test-scripts: PASS (native PowerShell fixture unavailable/skipped).go -C tools/rekor-entry test ./...: PASS; synthetic signed checkpoint/inclusion/SET and certificate-chain controls, including altered body/index/root/checkpoint/timestamp/UUID, rejected consistency and untrusted SAN.go -C tools/rekor-entry vet ./...,go -C tools/rekor-entry mod verifyand helper build: PASS.go installandrekor_monitor --help: PASS.kind: dssewith no artifact digest or identity authorization (correctly opaque/UNKNOWN). This did not create a signature or claim a watched-release match.openssl verifyof committed intermediate against committed roots: PASS.git diff --check: PASS.shellcheckis not installed locally. CI retains its ShellCheck job.Rollout stops and low-priority follow-ups
Draft only. Before rollout, review the pins/roots, initialize on main, observe real fleet and axiomatic canary-release entries, then wire and exercise the test-channel delivery before considering production alerts. No deployed behavior or live run→digest match is claimed by synthetic tests.
Follow-ups: (1) a dedicated-identity scheduled canary to close the initial-view/liveness gap; (2) compare DSSE payload hashes against payload bytes in published provenance/SBOM bundles; (3) scheduled reviewed TUF root refresh; (4) index OCI simple-signing payload hashes. Until (4), an attacker replay inside an image release's N budget remains undetected. An initial checkpoint's currency is unproven; a quiet log and a stalled feed are indistinguishable at first observation. A replay over already-recorded bytes remains indistinguishable. This monitor is a detection aid over published release data, not registry authorization enforcement.
TUF metadata recheck
The published timestamp metadata advertises snapshot 166; 166.snapshot.json has signed version 166 and targets version 14; 16.root.json has signed root-role version 16. These confirm the extraction provenance above; no version correction or trust-material change is needed. The root-role version is read from the root metadata itself, not the snapshot meta entry named root.json.