Security
Apothem security posture — OpenSSF Scorecard target, vulnerability reporting, hardening surfaces.
Apothem's security posture is built on five pillars: supply-chain hardening, vulnerability response, branch protection, release signing, and continuous security analysis. This section documents each surface.
Threat model
Apothem is a configuration materializer. It reads a shared profile and writes harness-native configuration files into the directories each harness reads. That single function bounds what it trusts and what it does not.
- What Apothem trusts. The shared profile you author and the bundled source tree placed by the installer. The profile is your declared intent; the engine treats it as authoritative input after schema validation.
- What Apothem does not trust. Profile contents reach the filesystem only
after validation: schema failures, unknown harness IDs, unsafe project paths,
missing manifest sources, target traversal, and symlink crossings all stop
before any write and return structured diagnostics with
files_written: []. - What materialized output never carries. No secrets. Profile diagnostics redact secret-like keys and token-shaped values before they reach plain output, JSON, logs, docs snippets, or tests, and public examples use fake placeholder identities and domains. Materialized configuration is meant to be committed and shared; it carries no credentials.
- The universal-deny floor. The Claude Code
settings.jsonthat Apothem materializes carries a non-negotiable deny floor regardless of any per-harness allow-list; on other harnesses the same floor is policy text in the materialized rules, not a settings-level deny list — secrets paths (.env*,~/.ssh/**, credential stores), destructive shell operations (rm -rf,sudo, force-pushes to protected branches), and execution of untrusted input. No allow-list silently widens this floor. - Blast radius. Writes are scoped to the harness directories and the Apothem-owned support subtrees. Existing managed targets are backed up before replacement and shared discovery directories are merged child-by-child, so unrelated operator-authored files remain in place.
Runtime safety posture
Harness lifecycle and profile-write commands validate before writing. Profile
schema failures, unknown harness IDs, unsafe project paths, missing manifest
sources, target traversal, and symlink crossings stop before filesystem writes
and return structured diagnostics with files_written: [].
Install and update operations preserve existing managed targets by backing them
up before replacement. Shared discovery directories are merged child-by-child so
unrelated operator-authored files remain in place. --dry-run performs the
same validation and reports planned outcomes without creating directories or
files.
Profile diagnostics redact secret-like keys and token-shaped values before they reach plain output, JSON output, logs, docs snippets, or tests. Public examples use fake placeholder identities and domains.
Quick links
- OpenSSF Scorecard posture — per-check evidence and known platform limits
- Webhooks audit — webhook-secret hygiene evidence
- Branch protection — pull-request requirement, required status checks, and force-push and deletion blocks
- Binary artifacts sweep — evidence that the source tree holds no binary files
- Security policy — vulnerability reporting & supported versions
- OpenSSF Scorecard badge — live score
Reporting a vulnerability
Use Private Vulnerability Reporting on the repository's Security tab. The full policy, supported-version matrix, and response timelines are at SECURITY.md.
Supply-chain release evidence
Every release artifact ships with:
- SLSA-3 build provenance via
slsa-framework/slsa-github-generator - Sigstore signatures via
sigstore/cosign-installer - CycloneDX SBOM of the shipped wheel and sdist, written by
scripts/release/generate_sbom.py: the apothem package, every package vendored underapothem/_vendorwith its license, the declared runtime requirements, and each distribution's SHA-256. The SBOM is signed and covered by the build provenance like the distributions - npm provenance statements via Trusted Publishing OIDC on
@ahmed-g-gad/apothem - GPG-signed git tags (EDDSA key
70396FED9C634163)
Verify a release
Download every asset of a release into an empty directory, then check the
signatures and the build provenance. Replace vX.Y.Z and X.Y.Z with the
release you downloaded.
gh release download vX.Y.Z --repo ahmed-g-gad/apothem
cosign verify-blob \
--bundle apothem-X.Y.Z-py3-none-any.whl.cosign.bundle \
--certificate-identity https://github.com/Ahmed-G-Gad/apothem/.github/workflows/release.yml@refs/tags/vX.Y.Z \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
apothem-X.Y.Z-py3-none-any.whl
slsa-verifier verify-artifact \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/Ahmed-G-Gad/apothem \
--source-tag vX.Y.Z \
apothem-X.Y.Z-py3-none-any.whl apothem-X.Y.Z.tar.gzcosign prints Verified OK and slsa-verifier prints PASSED: SLSA verification passed. Repeat the cosign command for any other asset with its
<asset>.cosign.bundle (earlier releases name the platform-archive and
SHA256SUMS bundles <asset>.sig). The darwin and linux archives are the same
bytes under two names, so SHA256SUMS lists one digest twice. From a clone,
bash scripts/release/verify-provenance.sh <download-dir> runs all three
checks, the SHA256SUMS manifest included, in one pass.
Two details decide whether verification means anything:
-
Owner casing. Identity checks are case-sensitive, and the certificates carry the owner as GitHub reports it:
Ahmed-G-Gad. A lowercase owner fails both tools. -
Exact identity. Prefer
--certificate-identitywith the full subject above. To accept any release tag, anchor the pattern at both ends:--certificate-identity-regexp '^https://github\.com/Ahmed-G-Gad/apothem/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'An unanchored pattern such as
github.com/Ahmed-G-Gad/apothemalso matches a forged subject that only contains that text, for example a workflow in another repository on a branch named after this one.
The release-cycle runbook records the same commands as the maintainer's post-release check.
Rebuild a release and compare
The wheel and sdist are reproducible: rebuilding the tag with the release toolchain gives the published SHA-256 digests. Use Python 3.12 on Linux, as the release does:
git clone https://github.com/ahmed-g-gad/apothem && cd apothem
git checkout vX.Y.Z
python3.12 -m venv .build && . .build/bin/activate
pip install --require-hashes -r scripts/release/requirements-build-linux.txt
export SOURCE_DATE_EPOCH="$(git log -1 --pretty=%ct)"
python -m build --no-isolation
python scripts/release/normalize_sdist.py dist/*.tar.gz
sha256sum dist/*Compare the digests with the published wheel and sdist. The build uses the
hash-locked setuptools that pyproject.toml pins, and normalize_sdist.py
removes the build time, file owners and checkout timestamps from the sdist.