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
56 changes: 45 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,14 @@ jobs:
cp /tmp/shim-cli-evidence-source/tests/corpus/guard-tools-v1.json dist/guard-tools-v1.json
- uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
path: dist/packages
path: /tmp/shim-cli-benchmark
format: cyclonedx-json
output-file: dist/shim-cli.sbom.cdx.json
upload-artifact: false
upload-release-assets: false
- name: Require the SBOM to list the package and every locked runtime dependency
run: |
python -c 'import json, os, re; from pathlib import Path; version = os.environ["GITHUB_REF_NAME"].removeprefix("v"); found = {(re.sub(r"[-_.]+", "-", component["name"]).lower(), component.get("version")) for component in json.loads(Path("dist/shim-cli.sbom.cdx.json").read_text())["components"] if component["type"] == "library"}; locked = {(re.sub(r"[-_.]+", "-", name).lower(), pin) for name, pin in re.findall(r"^([A-Za-z0-9][A-Za-z0-9._-]*)==(\S+) \\$", Path("dist/requirements.lock").read_text(), re.MULTILINE)}; assert ("shim", version) in found; assert locked and locked <= found, sorted(locked - found)'
- name: Recheck the package artifact allowlist
run: |
python -c 'import os; from pathlib import Path; version = os.environ["GITHUB_REF_NAME"].removeprefix("v"); expected = {f"shim-{version}-py3-none-any.whl", f"shim-{version}.tar.gz"}; assert {path.name for path in Path("dist/packages").iterdir()} == expected'
Expand All @@ -150,12 +153,6 @@ jobs:
cd ..
sha256sum COMPATIBILITY.md shim.pyz guard-v2.json guard-v2-metrics.json guard-tools-v1.json benchmark-hook.json requirements.lock shim-cli.sbom.cdx.json
) > dist/SHA256SUMS
- name: Verify checksums from a flat download directory
run: |
mkdir dist/download
cp "dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl" "dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz" dist/COMPATIBILITY.md dist/shim.pyz dist/guard-v2.json dist/guard-v2-metrics.json dist/guard-tools-v1.json dist/benchmark-hook.json dist/requirements.lock dist/shim-cli.sbom.cdx.json dist/SHA256SUMS dist/download/
cd dist/download
sha256sum -c SHA256SUMS
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: release-dist
Expand Down Expand Up @@ -187,18 +184,48 @@ jobs:
- name: Verify the package artifact allowlist
run: python -c 'import os; from pathlib import Path; version = os.environ["GITHUB_REF_NAME"].removeprefix("v"); expected = {f"shim-{version}-py3-none-any.whl", f"shim-{version}.tar.gz"}; assert {path.name for path in Path("dist/packages").iterdir()} == expected'
- uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
id: provenance
with:
subject-path: dist/packages/*
subject-path: |
dist/packages/*
dist/shim.pyz
- uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
id: sbom
with:
subject-path: dist/packages/*
subject-path: |
dist/packages/*
dist/shim.pyz
sbom-path: dist/shim-cli.sbom.cdx.json
- name: Attach and hash the attestation bundles
env:
PROVENANCE_BUNDLE: ${{ steps.provenance.outputs.bundle-path }}
SBOM_BUNDLE: ${{ steps.sbom.outputs.bundle-path }}
run: |
cp "$PROVENANCE_BUNDLE" "dist/shim-${GITHUB_REF_NAME#v}.intoto.jsonl"
cp "$SBOM_BUNDLE" "dist/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl.sigstore.json"
cp "$PROVENANCE_BUNDLE" dist/shim.pyz.sigstore.json
(
cd dist
sha256sum "shim-${GITHUB_REF_NAME#v}.intoto.jsonl" "shim-${GITHUB_REF_NAME#v}-py3-none-any.whl.sigstore.json" shim.pyz.sigstore.json
) >> dist/SHA256SUMS
- name: Verify checksums from a flat download directory
run: |
mkdir /tmp/shim-cli-download
cp "dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl" "dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz" dist/COMPATIBILITY.md dist/shim.pyz dist/guard-v2.json dist/guard-v2-metrics.json dist/guard-tools-v1.json dist/benchmark-hook.json dist/requirements.lock dist/shim-cli.sbom.cdx.json "dist/shim-${GITHUB_REF_NAME#v}.intoto.jsonl" "dist/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl.sigstore.json" dist/shim.pyz.sigstore.json dist/SHA256SUMS /tmp/shim-cli-download/
cd /tmp/shim-cli-download
sha256sum -c SHA256SUMS
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: attested-dist
path: dist/
if-no-files-found: error

release:
needs: [build, attest]
runs-on: ubuntu-latest
environment: release
permissions:
attestations: read
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
Expand All @@ -211,13 +238,20 @@ jobs:
test -f "docs/releases/${GITHUB_REF_NAME#v}.md"
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: release-dist
name: attested-dist
path: dist
- name: Verify the package artifact allowlist
run: python -c 'import os; from pathlib import Path; version = os.environ["GITHUB_REF_NAME"].removeprefix("v"); expected = {f"shim-{version}-py3-none-any.whl", f"shim-{version}.tar.gz"}; assert {path.name for path in Path("dist/packages").iterdir()} == expected'
- name: Verify the attestations before publishing
env:
GH_TOKEN: ${{ github.token }}
run: |
gh attestation verify "dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl" --repo "$GITHUB_REPOSITORY"
gh attestation verify "dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz" --repo "$GITHUB_REPOSITORY"
gh attestation verify dist/shim.pyz --repo "$GITHUB_REPOSITORY"
- env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$GITHUB_REF_NAME" "dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl" "dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz" dist/COMPATIBILITY.md dist/shim.pyz dist/guard-v2.json dist/guard-v2-metrics.json dist/guard-tools-v1.json dist/benchmark-hook.json dist/requirements.lock dist/SHA256SUMS dist/shim-cli.sbom.cdx.json --notes-file "docs/releases/${GITHUB_REF_NAME#v}.md" --repo "$GITHUB_REPOSITORY"
run: gh release create "$GITHUB_REF_NAME" "dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl" "dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz" dist/COMPATIBILITY.md dist/shim.pyz dist/guard-v2.json dist/guard-v2-metrics.json dist/guard-tools-v1.json dist/benchmark-hook.json dist/requirements.lock dist/SHA256SUMS dist/shim-cli.sbom.cdx.json "dist/shim-${GITHUB_REF_NAME#v}.intoto.jsonl" "dist/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl.sigstore.json" dist/shim.pyz.sigstore.json --notes-file "docs/releases/${GITHUB_REF_NAME#v}.md" --repo "$GITHUB_REPOSITORY"

publish:
needs: [build, attest, release]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ Every figure here was measured on the released build, not estimated.
| Hook cost | **67 ms** median end to end, interpreter start included; **41 ms** for a session summary |
| With 32 custom patterns | **+0.8 ms** median against the same prompt with none |
| Detector corpus | **570 cases**, graded on exact redacted output rather than category presence |
| Release evidence | SBOM, provenance and Sigstore bundles on the release page from 0.3.2, with the `gh attestation verify` command in [the compatibility record](https://github.com/GetSHIM/shim-cli/blob/main/docs/compatibility.md#030-release-evidence) |

<p align="center">
<img src="https://raw.githubusercontent.com/GetSHIM/shim-cli/main/docs/assets/shots/shim-doctor.png" width="880"
Expand Down
4 changes: 4 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ relevant redacted logs. Please avoid sending real secrets or personal data. We
will acknowledge the report, assess it, and coordinate disclosure through the
advisory.

To verify a downloaded release against its attestation bundles, run the
`gh attestation verify` command in
[the compatibility record](docs/compatibility.md#030-release-evidence).

## Scope notes

The hook is a local best-effort guard, not an enforcement boundary. A report
Expand Down
1 change: 1 addition & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Recorded 8 September 2026 on macOS 26.4 arm64, CPython 3.13.5, uv 0.12.5.
| Context diet under the proxy | Seven scripted Claude Code sessions, 8 September 2026. The cache prefix held in every one, including a session whose configuration changed mid-run. A 22,199-byte tool result became 13,374 with the diet on and 21,690 with it off. [Study](study-2026-09-08-image-repeat-cache.md). |
| Python floor | 3.10 is exercised by CI only; no local 3.10 run was made. The bundled archive targets 3.9 and is rebuilt and compared by a contract test. |
| Supply-chain workflows | CodeQL, Scorecard, Dependabot and the prose check are configured and their pinning is asserted by `tests/contracts/test_workflows.py`. They run on pull requests into `main` and on pushes to `main`; a first green run of each is a condition of the release, not a claim of this document. |
| SBOM and attestations, from 0.3.2 | `shim-cli.sbom.cdx.json` is scanned from the tagged wheel installed with `requirements.lock`, and the release fails unless it lists `shim` at the tag's version and every unconditional pin in the lock: **11 library components** (`shim` and ten dependencies; `tomli` and `colorama` are conditional and absent on CPython 3.13), measured with syft 1.42.3 on the tagged tree. The wheel, sdist and `shim.pyz` are attested, the release job verifies all three before publishing, and the bundles are release assets. Against a downloaded wheel: `gh attestation verify shim-0.3.2-py3-none-any.whl --bundle shim-0.3.2.intoto.jsonl --repo GetSHIM/shim-cli` for provenance, and the same with `--bundle shim-0.3.2-py3-none-any.whl.sigstore.json --predicate-type https://cyclonedx.org/bom` for the SBOM. |

## Dated development evidence

Expand Down
32 changes: 32 additions & 0 deletions docs/releases/0.3.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,35 @@ unchanged; a mixed one says `3 of 4 requests on a subscription`. The dollar
figure stays on a subscription: it is what the same traffic would cost on an
API key. `shim watch --json` carries `spend_basis` at the top and `auth_route`
and `response_scan_reason` on each exchange.

## Release evidence that opens

The SBOM on the 0.3.1 release page was 409 bytes and listed nothing: it was
scanned from a directory holding only the wheel and the source archive, which
syft does not open. It is now scanned from the tagged wheel installed with
`requirements.lock`, and the release fails unless it lists `shim` at the tag's
version and every unconditional pin in the lock.

The release page now also carries the Sigstore bundles that were until now only
in GitHub's attestation store: `shim-0.3.2.intoto.jsonl` for the provenance of
the wheel, the source archive and `shim.pyz`,
`shim-0.3.2-py3-none-any.whl.sigstore.json` for the SBOM attestation, and
`shim.pyz.sigstore.json` for the archive. All three are in `SHA256SUMS`, and
the release job verifies the wheel, the source archive and `shim.pyz` against
their attestations before it publishes. To check a downloaded wheel:

```
gh attestation verify shim-0.3.2-py3-none-any.whl --bundle shim-0.3.2.intoto.jsonl --repo GetSHIM/shim-cli
```

## Verified

`python scripts/check.py` green on 3.13: 2,005 tests, lint, format, types, and
a package build; CI covers 3.10. The committed zero-install archive is byte
identical to a fresh build and answers identically on Python 3.9 and 3.13,
including for non-ASCII text. No open Dependabot alerts; the six open
code-scanning alerts are Scorecard repository checks with no file attached.

Client versions are unchanged from 0.3.0, and the model-facing masking note
was exercised live on Claude Code 2.1.263; see
[compatibility.md](../compatibility.md).
60 changes: 60 additions & 0 deletions tests/contracts/test_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,63 @@ def test_a_workflow_starts_from_least_privilege(workflow: Path) -> None:
assert text.count("persist-credentials: false") == checkouts, (
f"{workflow.name}: a checkout keeps its credentials"
)


RELEASE = WORKFLOWS / "release.yml"


def _step(marker: str) -> str:
text = RELEASE.read_text(encoding="utf-8")
start = text.rindex("\n - ", 0, text.index(marker))
end = text.find("\n - ", start + 1)
return text[start:end]


def test_the_sbom_scans_the_installed_wheel_and_is_checked() -> None:
text = RELEASE.read_text(encoding="utf-8")
sbom = _step("uses: anchore/sbom-action@")
check = _step("name: Require the SBOM to list")

assert "path: /tmp/shim-cli-benchmark" in sbom
assert "dist/packages" not in sbom
assert text.index('--no-deps "dist/packages/shim-') < text.index(sbom)
assert text.index(sbom) + len(sbom) == text.index(check)
assert '["components"]' in check and "dist/requirements.lock" in check


def test_the_attestation_bundles_are_hashed_and_attached() -> None:
bundles = (
'"dist/shim-${GITHUB_REF_NAME#v}.intoto.jsonl"',
'"dist/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl.sigstore.json"',
"dist/shim.pyz.sigstore.json",
)
hashed = _step("name: Attach and hash the attestation bundles")
flat = _step("name: Verify checksums from a flat download directory")
create = _step("gh release create")

assert all(bundle in flat and bundle in create for bundle in bundles)
assert ">> dist/SHA256SUMS" in hashed
assert all(
name in hashed
for name in (".intoto.jsonl", ".whl.sigstore.json", " shim.pyz.sigstore.json")
)
attest = [_step("id: provenance"), _step("id: sbom")]
assert all("dist/packages/*" in step and "dist/shim.pyz" in step for step in attest)


def test_the_release_verifies_attestations_before_it_publishes() -> None:
text = RELEASE.read_text(encoding="utf-8")
verify = _step("gh attestation verify")

assert text.index(verify) < text.index("gh release create")
assert (
"attestations: read"
in text[text.index("\n release:") : text.index("\n publish:")]
)
assert verify.count("gh attestation verify ") == 3
for subject in (
'"dist/packages/shim-${GITHUB_REF_NAME#v}-py3-none-any.whl"',
'"dist/packages/shim-${GITHUB_REF_NAME#v}.tar.gz"',
"dist/shim.pyz",
):
assert f'gh attestation verify {subject} --repo "$GITHUB_REPOSITORY"' in verify