… review
Codex + Gemini security review of the original commit (cross-referenced against
GitHub Actions OIDC docs and sigstore/cosign source) surfaced material gaps that
would have shipped if the original `=1` opt-in landed as-is. Each fix is paired
with a test that confirms the behavior.
## What changed (and why)
1. Reject `SIMPLE_CONTAINER_ALLOW_PREVIEW=1` entirely.
The original "=1 widens to any branch" form is gone. It trusted
`branch-preview.yaml@refs/heads/.+` — anyone with push access to ANY branch
in the repo could ship a tarball that the regex would accept, once the user
opted in for "their" branch. That's a much broader trust radius than picking
up an unreviewed feature branch you actually want to test (push to main is
gated by branch protection + required reviews; push to a feature branch is
not). The `=1` form now fails closed with a pointer to `_BRANCH`.
2. Require `SIMPLE_CONTAINER_TRUST_PREVIEW_BRANCH=<branch>` for preview installs.
Pins cosign verification to one specific branch's branch-preview.yaml
signature. A different branch's tarball at the CDN URL — whether served
deliberately by a CDN-account compromise (T4) or accidentally cached —
fails verification.
Branch name is validated against a conservative allowlist
(`^[A-Za-z0-9._/-]+$`) BEFORE interpolation into the cosign regex, plus
explicit `..` / leading-/trailing-`/` / `.lock`-suffix rejections. This
neutralizes the regex-injection / "identity shadowing" path Gemini
flagged: a branch name like `evil.*` would otherwise re-open the
permissive trust set, and `feat.foo` would shadow `feat/foo`. The single
regex metachar that the allowlist still permits (`.`) is escaped to `\.`
in the generated regex.
3. Optional `SIMPLE_CONTAINER_TRUST_PREVIEW_SHA=<40-hex>` to pin the commit.
Maps to cosign's `--certificate-github-workflow-sha`, which verifies
against the Sigstore certificate's GitHub OIDC `workflow_sha` claim
(OID 1.3.6.1.4.1.57264.1.3). Closes T2 (workflow content not pinned at
the identity layer) and the residual T4 (CDN can serve an old tarball
signed from the same branch with a different SHA). Recommended for CI;
optional for ad-hoc developer installs.
SHA value is validated as 40-char lowercase hex before being passed to
cosign so an invalid input fails the install with a clear message,
not a confusing cosign error.
4. Loud stderr warning on every preview install.
Mitigates T3 (persistent `export` in shell rc silently relaxes trust).
The warning is unconditional and visible on every invocation when
_BRANCH is set, listing the exact branch trusted and whether the SHA
pin is in effect.
5. Smarter error message: extract signer branch from cosign output.
When verification fails because the tarball is preview-signed but no
_BRANCH is set, parse the signer ARN out of cosign's "got subjects [...]"
line and offer it back to the user as the env-var value to copy-paste.
Removes the curl|sh example string that tripped the org's semgrep
`shell-curl-pipe-to-shell` rule — the user already knows how they
invoked us, no need to demo it.
## Threats explicitly preserved as out-of-scope
- "Same CDN namespace" (preview artifacts share production object keys) —
this is a publish-side concern, belongs in branch-preview.yaml not sc.sh.
- "Verify installed `sc --version` matches requested" — sc.sh already prints
the post-install version; explicit interlock is a separate hardening pass.
- "Path-traversal-safe extraction" — pre-existing concern in extract logic,
orthogonal to verification regex.
## Verified end-to-end
8 manual test cases against the live preview tarball
v2026.5.26-pre.4cc1a03-preview.4cc1a03 (signed by branch-preview.yaml@feat/
cloudtrail-alerts-exclusions-and-new-detectors with SHA
4cc1a03):
A. Strict mode (no env vars): rejected with auto-extracted branch hint
B. Deprecated `=1`: rejected BEFORE cosign runs (fail-fast)
C. Invalid branch (`evil.*`): rejected at validation
D. Wrong branch (valid format, doesn't match signer): cosign mismatch
E. Correct branch: success + warning
F. Correct branch + correct SHA pin: success + SHA-pinned warning
G. Correct branch + wrong SHA: cosign rejects (`workflow SHA not found`)
H. Invalid SHA format: rejected at validation
Semgrep `shell-curl-pipe-to-shell` rule passes on the revised script
(simple-container-com/actions semgrep-scan/rules/shell.yml).
Refs codex+gemini cross-review of the original sc.sh patch.
Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Why
Production users testing a feature-branch SC build via
sc.share blocked by the Phase 2c failgate: the cert-identity regex passed tocosign verify-blobis hard-pinned topush.yaml@refs/heads/main. Preview tarballs published bybranch-preview.yamlare legitimately Sigstore-signed but cannot install. The only workarounds today arecurl + tar -xz(bypasses signature verification entirely) or fork sc.sh locally. Both are worse than a documented, narrowly-scoped opt-in.Discovered while validating the CloudTrail security alerts plugin (PR #277) against a preview build of that branch.
What
sc.sh— branch-pinned (and optionally SHA-pinned) opt-inverify_sc_tarballwidens the accepted identity regex to ALSO acceptbranch-preview.yaml@refs/heads/<that exact branch>. The branch name is allowlist-validated (^[A-Za-z0-9._/-]+$, no../ leading-trailing-slash /.lock-suffix) and the.regex metachar is escaped before interpolation, so a value likeevil.*orfeat.foocannot re-open the permissive trust set.When
_SHAis set, cosign is given--certificate-github-workflow-shawhich verifies against the Sigstore certificate's GitHub OIDCworkflow_shaclaim (OID1.3.6.1.4.1.57264.1.3) — pinning verification to a specific commit, not the mutable branch head.sc.sh— rejects the over-permissive=1form outrightThe prior commit had
SIMPLE_CONTAINER_ALLOW_PREVIEW=1(any branch). Codex + Gemini both flagged that as a meaningful trust expansion — anyone with push access to any branch in the repo could ship a tarball that the regex would accept once a user opted in for "their" branch. Now=1fails closed with a pointer to_BRANCH.sc.sh— loud stderr warning + smarter error messagesgot subjects [...]line, and offers it back as a copy-paste env-var value.docs/SECURITY.md— documentationNew subsection under "Verifying tarballs" documenting:
SIMPLE_CONTAINER_TRUST_PREVIEW_BRANCH+SIMPLE_CONTAINER_TRUST_PREVIEW_SHAusagecosign verify-blobequivalent for preview tarballs (branch + SHA pin)Security analysis
Threats addressed:
_BRANCHrequired;=1rejected; branch allowlist + regex escape_SHAvia--certificate-github-workflow-sha(verifies OID 1.3.6.1.4.1.57264.1.3)_BRANCHvalue must change per preview, narrowing the persistence window.metacharThreats explicitly out of scope (separate concerns, documented in commit message):
sc --versionvs requested interlock (defense-in-depth, separate hardening pass)Testing
8 manual test cases against the live preview tarball
v2026.5.26-pre.4cc1a03-preview.4cc1a03:ALLOW_PREVIEW=1_BRANCH=evil.*_BRANCH=feat/wrong(valid format, doesn't match signer)_BRANCHonly_BRANCH+ correct_SHA_BRANCH+ wrong_SHA(40-hex zeros)_BRANCH+ invalid_SHA(not-a-sha)Semgrep
shell-curl-pipe-to-shellrule: 0 findings (curl|sh example was removed from error text).Test plan
_BRANCH+_SHApinRefs