Supply chain
Provenance, vulnerability scanning, and image pinning — how gittan protects your software supply chain.
Provenance
Every pipeline that publishes a container image automatically generates a SLSA v1 provenance attestation. This is not opt-in. If you publish an image through gittan, it gets provenance.
gittan uses a two-layer signing scheme: the platform key signs an ephemeral certificate for the run, and the ephemeral key signs the attestation. The private key exists only for the duration of the pipeline run. A compromised runner cannot sign future builds.
Attestations are pushed as OCI referrer artifacts alongside the image, following
the OCI 1.1 Referrers API. The verify-provenance step runs automatically after publish.
Secret scanning
Every push is scanned for hardcoded secrets using gitleaks. API keys, tokens, private keys, and other credentials are detected automatically. If secrets are found, the push is blocked — no bypass, no override, no emergency exception.
The scanner excludes node_modules, vendor, .pnpm-store, and .yarn directories. If a finding is a
false positive, the right fix is a gitleaks allowlist in your repo, not a
platform bypass.
Vulnerability scanning
gittan runs Trivy on every push as a platform policy. CRITICAL CVEs block the push — the pipeline fails and the developer sees what needs fixing. HIGH findings are reported but do not block. Scans produce structured output that gittan parses into per-repo findings visible in the team's security view.
Reachability analysis
A CVE in a package you depend on is not the same as a CVE in code you actually call. gittan's dependency scanner includes LLM-powered reachability analysis that goes beyond severity ratings: it identifies which source files import the vulnerable package, analyzes whether the vulnerable function is actually invoked, and checks whether user-controlled data reaches it.
Reachable vulnerabilities with a fix available block the push. Unreachable vulnerabilities are downgraded to informational — tracked in the team's security view but not blocking. When the analysis cannot determine reachability (LLM unavailable or low confidence), findings fall back to severity-based classification.
See security findings for the full reachability classification table.
Policy scan
Every push also runs a static analysis pass that checks for security anti-patterns and tooling choices. This scan is advisory — it reports findings but never blocks the push. Findings go to the team's security view.
The policy scan flags patterns like:
- LLM response passed to
eval()orexec()— code injection risk - User input interpolated into LLM prompts — prompt injection risk
innerHTMLassignments — XSS risk- String interpolation in database queries — SQL injection risk
- Privileged containers or privilege escalation in Kubernetes manifests
:latesttags in Dockerfiles
Container image scanning
When a pipeline publishes a container image, gittan automatically runs Trivy against the published image. This catches vulnerabilities in OS packages, system libraries, and anything else baked into the image that dependency scanning cannot see.
CRITICAL image CVEs block the push, same as dependency scanning. HIGH findings go to the team's security view.
Image provenance chain
gittan base images (gittan/node:24, gittan/python:3.14, etc.) are built with
a verified chain from upstream to your pipeline:
| Stage | What happens | If verification fails |
|---|---|---|
| 1. Upstream pull | Official image pulled from Docker Hub (e.g. node:24-alpine).
Cosign verifies the upstream Sigstore signature before the build starts. | Image is not pulled. Build aborted. |
| 2. gittan build | gittan layers its base tools on top. The build pipeline signs the resulting image
using keyless Sigstore signing — the signing identity is the pipeline's
OIDC workload token from auth.gittan.eu. | — |
| 3. Registry push | Signed image and SLSA provenance attestation are pushed to images.gittan.eu as OCI referrer artifacts. | — |
| 4. Runtime verify | Before running any pipeline step, the runner verifies the image signature against gittan's OIDC issuer. Every pull is verified, every time. | Step does not run. Pipeline rejected. |
No key management. The signing identity is the pipeline run's short-lived OIDC token,
verified through Sigstore's transparency log. Anyone can verify that a gittan/* image was built by gittan's CI —
not manually pushed, not tampered with after build.
Requiring provenance for all images
gittan base images are always verified. For everything else, org owners can enforce provenance verification as an org-level policy:
# org-pipelines/policies/require-provenance.yaml
description: Reject unsigned container images in all pipelines
enabled: true
match:
all: true
settings:
require-image-provenance: true
trusted-issuers:
- https://auth.gittan.eu # gittan CI
- https://accounts.google.com # Docker official images
- https://token.actions.githubusercontent.com # GitHub Actions When enabled, the pipeline runner verifies the cosign signature on every image before running the step. The behavior per image type:
| Image source | Signed by | Result |
|---|---|---|
| gittan/node:24 | gittan CI (auth.gittan.eu) | Passes |
| images.gittan.eu/myorg/myapp:1.0 | Team's pipeline (auth.gittan.eu) | Passes — same OIDC issuer |
| node:24-alpine | Docker (accounts.google.com) | Passes — if issuer is in trusted list |
| random/tool:latest | Not signed | Rejected — no signature found |
| evil.registry.io/backdoor:1 | Signed by unknown issuer | Rejected — issuer not in trusted list |
The trusted-issuers list controls which OIDC
issuers are accepted. Only add issuers you trust to produce images your teams should run.
Teams cannot override this — it is enforced at the pipeline runtime level, not
in the image or the step configuration.
# Verify any image manually
cosign verify images.gittan.eu/myorg/myimage:1.0 \
--certificate-oidc-issuer https://auth.gittan.eu Image pinning
No :latest tag is allowed. Vetted images are
pinned by digest. User images must be pinned. A mutable tag is a supply chain attack
waiting to happen.