Repository navigation
Description
π€ Generated by the Agentic Engineer
Evidence
ci.yaml's βοΈ Setup KSail step downloads the KSail release archive from a version-pinned URL, then extracts it and sudo installs it β with no integrity check:
curl -fsSL "https://github.com/devantler-tech/ksail/releases/download/v${KSAIL_VERSION}/ksail_${KSAIL_VERSION}_linux_amd64.tar.gz" -o /tmp/ksail.tar.gz
tar -xzf /tmp/ksail.tar.gz -C /tmp
sudo install /tmp/ksail /usr/local/bin/ksail
A version-pinned URL is not an integrity check: it names which asset to fetch, not what bytes to expect. A replaced release asset would be installed and executed by the workflow.
Raised by CodeRabbit against the identical step newly added in #2830, where it has been fixed. This issue covers the pre-existing instance in ci.yaml, deliberately left out of that PR so a supply-chain change does not ride along inside a PR about Kubescape SARIF publishing.
Affected audience and impact
Every pull_request and merge_group run of ci.yaml that hits the validate job. merge_group runs gate merges to main, so the binary runs on a trusted path.
Expected behaviour
The archive is verified before it is extracted or installed, and a verification that cannot be performed fails the step rather than being skipped.
Acceptance criteria
- The
validatejob verifiesksail_${KSAIL_VERSION}_linux_amd64.tar.gzagainst the release's publishedksail_${KSAIL_VERSION}_checksums.txtbeforetar -xzf. - A missing checksum entry fails the step with an actionable message β it must not silently skip verification. Note
grepexits 1 underset -euo pipefail, so the lookup needs|| trueor the step dies before the message prints. - A checksum mismatch fails the step and names the expected and actual digests.
- Verified three ways before merge: real archive passes, a tampered archive fails, a missing entry fails closed.
The implementation in #2830's validate-main.yaml kubescape-baseline job is a working reference β this is the same block.
Deliberately NOT a pinned literal SHA
KSAIL_VERSION is Renovate-managed (datasource=github-releases, group ksail). Renovate bumps the version but cannot update an opaque digest beside it, so a pinned SHA breaks CI on every bump until hand-edited β a control that reliably breaks the routine path is one that gets removed.
Stated limit: archive and checksum list share an origin, so this detects a corrupted, truncated or individually-substituted asset, not a fully compromised release.
Stronger follow-up (measured, not speculative)
KSail publishes an attestation for the exact asset digest, but it is an https://in-toto.io/attestation/release/v0.2 predicate and gh attestation verify returns no attestations found for it β with the default predicate and with that one passed explicitly. So attestation-based verification is not a drop-in today; it needs its own investigation.
Size
Small β one run: block, plus the three-way verification.
Metadata
Metadata
Assignees
Labels
Type
Fields
Projects
- StatusShow more project fieldsβ Done
Activity
coderabbitai commentedon Jul 28, 2026
π Related PRs
#2319 - feat(security): cosign-verify the infrastructure Flux OCI artifact [merged]
#2502 - feat(ci): validate embedded JSON blobs in ConfigMaps [merged]
#2538 - fix(ci): cache kubeconform schemas in Validate Manifests job [merged]
#2625 - fix(deploy): synchronize production GHCR pull auth [merged]
#2633 - fix(deploy): verify incoming image on Talos nodes [merged]
π Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.π§ͺ Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:π¬ Have feedback or questions? Drop into our discord!
devantler commentedon Aug 10, 2026
Correction to this issue's own premise, found while fixing it (#3073).
The body says "
merge_groupruns gate merges tomain, so the binary runs on a trusted path."That is wrong for this job.
ci.yaml'svalidatejob is gated ongithub.event_name == 'pull_request', so it never runs formerge_groupβ the workflow carries thattrigger, but this job does not accept it. CodeRabbit caught the same thing in my PR comment, where I
had repeated the claim.
The exposure is still real, just different:
validateruns on fork PRs, and the archive issudo installed and executed on the runner. The fix stands on that.The merge-gating claim belongs to the deploy paths, which install the same archive with no check
at all β now tracked as #3075 (and #3076 for
talosctl, which has no check anywhere). Those are thehigher-severity instances of this class; this one was the least privileged of four.
Generalisable: verify a threat statement against the job condition, not the workflow's
on:list. A workflow-level trigger says nothing about which jobs actually accept it.