Skip to content

πŸ›‘οΈ Weekly Security Audit β€” 2026-06-15Β #35

@github-actions

Description

@github-actions

Now I have a comprehensive picture of the codebase. Let me compile the security audit report.

Security Audit Report

Executive Summary

This is a well-engineered cryptographic library with strong security fundamentals. The codebase demonstrates awareness of advanced cryptographic threats (partitioning oracles, nonce budget exhaustion, key commitment), uses established primitives from the @noble/* family, and implements important safety rails (verify-after-encrypt, constant-time comparisons, SecureBuffer with mlock, indistinguishable authentication errors).

No Critical findings were identified. The library's cryptographic architecture is sound β€” nonces are CSPRNG-sourced, AAD binds all relevant fields, key separation is enforced via HKDF with distinct info strings, and commitment prevents multi-key attacks.

A small number of Medium and Low findings are noted below, primarily around CI/CD configuration and minor hardening opportunities.


Critical Findings

None.


High Findings

None.


Medium Findings

M-1: pull_request_target with secrets: inherit in Dependabot Handler

File: .github/workflows/dependabot-handler.yml:13,25

Description: The workflow triggers on pull_request_target and passes secrets: inherit to a reusable workflow at de-otio/.github. While gated by if: github.actor == 'dependabot[bot]', this pattern inherits all repository secrets into a context where code from the PR (not the base branch) could potentially be executed if the called reusable workflow checks out the PR head ref.

Attack scenario: If the reusable workflow at de-otio/.github/.github/workflows/dependabot-claude-review.yml@main uses actions/checkout with ref: ${{ github.event.pull_request.head.ref }} and then runs arbitrary code (npm scripts, test hooks), a compromised Dependabot commit could exfiltrate secrets. The dependabot[bot] actor check narrows the window significantly (an attacker must compromise Dependabot itself), but the pattern is flagged by GitHub's own security guidance.

Fix: Audit the called workflow to ensure it never checks out the PR head or runs user-controlled code with inherited secrets. Alternatively, pass only the specific secrets needed rather than secrets: inherit.


M-2: InMemoryMessageCounter Resets Across Restarts β€” AES-GCM Nonce Budget Bypass

File: src/message-counter.ts:41-73; src/envelope-client.ts:140

Description: The default MessageCounter is in-memory and documented as unsafe for multi-process use. However, the library defaults to it when no counter is provided. If a consumer uses AES-256-GCM with a long-lived key across process restarts (serverless, workers, container restarts), the counter resets to 0 each time β€” the 2Β³Β² nonce budget could be silently exceeded without triggering NonceBudgetExceeded.

Attack scenario: A chaoskb deployment using AES-256-GCM (selected for FIPS interop) on AWS Lambda processes ~10k messages/day. Over 1.2 years with frequent cold starts, the actual cumulative message count per key exceeds 2Β³Β² but the in-memory counter never reaches the cap. Random nonce collision probability rises, potentially enabling AES-GCM forgery (Joux multi-collision on shared nonces).

Mitigation already present: The library emits a console.warn on first use, and XChaCha20-Poly1305 (the default) is unaffected. The code comments document this well.

Recommended fix: Consider making MessageCounter a required parameter when algorithm: 'AES-256-GCM' is selected, rather than silently falling back to in-memory. This would force callers to acknowledge the operational requirement.


M-3: Unpinned npm install -g @anthropic-ai/claude-code in CI Workflows

File: .github/workflows/security-review.yml:52, .github/workflows/weekly-security-audit.yml:54

Description: The security review and weekly audit workflows install @anthropic-ai/claude-code globally without a version pin. A supply chain compromise of this package (typosquatting, maintainer account takeover, or registry hijack) would execute arbitrary code with repository permissions (contents: read, pull-requests: write, issues: write, id-token: write).

Attack scenario: An attacker compromises the @anthropic-ai/claude-code npm package. On the next PR or weekly schedule trigger, the compromised package runs with OIDC tokens that grant AWS Bedrock access and GitHub write permissions, enabling silent exfiltration or code injection.

Fix: Pin to a specific version: npm install -g @anthropic-ai/claude-code@0.2.x (or use a hash-pinned lockfile approach). Better: vendor or cache the package in a controlled artifact.


Low Findings

L-1: JSON.parse of Decrypted Plaintext Without Prototype Pollution Guard

File: src/envelope/v1.ts:154

Description: After successful AEAD decryption, the plaintext is parsed with JSON.parse(...) and cast to Record<string, unknown>. If the plaintext contains "__proto__" or "constructor" keys, downstream consumers that perform naive property spreading could be vulnerable to prototype pollution.

Impact: Low β€” this is an authenticated path (only the legitimate key holder can produce parseable plaintext), and the vulnerability is in the consumer code, not this library. However, the library could defensively strip dangerous keys.

Fix (optional): This is informational. Documenting that consumers should use Object.create(null) when spreading decrypted payloads into sensitive contexts is sufficient. The library should not alter plaintext content.


L-2: esbuild Dev Dependency Has Known High-Severity Vulnerability

File: package.json:49 (devDependencies)

Description: npm audit reports a high-severity vulnerability in esbuild (GHSA-gv7w-rqvm-qjhr β€” missing binary integrity verification enabling RCE via NPM_CONFIG_REGISTRY, and GHSA-g7r4-m6w7-qqqr β€” arbitrary file read on Windows dev server).

Impact: Low in context β€” esbuild is a devDependency used only in the regen-vectors script. It does not ship with the library, is not used in CI tests, and the vulnerabilities require either a malicious registry or running a Windows dev server. No production or consumer risk.

Fix: npm audit fix or pin esbuild to a patched version when available.


L-3: Claude Agent Workflow Has Empty allowed_non_write_users

File: .github/workflows/claude-agent.yml:67

Description: The allowed_non_write_users: "" setting means only users with write access to the repository can trigger the Claude agent via @claude mentions. This is the correct restrictive default, but worth documenting that any future addition of usernames here expands the attack surface (an allowed non-write user could instruct Claude to push malicious code).

Impact: Currently safe. Informational for future maintenance.


L-4: test-slow Job Uses Unpinned actions/checkout@v4 (Inconsistent with Others)

File: .github/workflows/ci.yml:52

Description: The test-slow job uses actions/checkout@v4 while all other jobs use @v6. This is likely a typo/oversight. While not a direct vulnerability (both resolve to maintained major tags), consistency ensures all jobs receive the same security patches.

Fix: Change to actions/checkout@v6 for consistency.


Dependency Review

Dependency Version Type Risk Assessment
@noble/ciphers ^2.2.0 runtime Low risk β€” audited, no known CVEs, minimal dependency tree
@noble/hashes ^2.2.0 runtime Low risk β€” audited, no known CVEs, widely reviewed
cborg ^5.1.0 runtime Low risk β€” CBOR codec, no known CVEs
sodium-native ^5.1.0 runtime Low risk β€” native binding to libsodium, well-maintained
esbuild ^0.28.0 dev Medium β€” known vuln (GHSA-gv7w-rqvm-qjhr), dev-only
vitest ^4.1.4 dev Low risk β€” test framework, dev-only
typescript ~6.0.0 dev Low risk β€” compiler, dev-only
@biomejs/biome ^1.9.4 dev Low risk β€” linter, dev-only

The runtime dependency graph is minimal (4 packages) and well-vetted. @noble/* packages are authored by Paul Miller, audited, and are the standard recommendation for JS cryptography. sodium-native is a mature native binding. cborg is a focused CBOR codec with no transitive dependencies of concern.


Positive Security Observations

These design choices significantly raise the bar:

  1. Verify-after-encrypt (src/envelope/v1.ts:81-86) β€” catches AEAD implementation bugs before releasing ciphertext
  2. Indistinguishable authentication errors (src/errors.ts:122-129) β€” prevents partitioning oracle attacks
  3. HKDF key separation with distinct info strings (src/primitives/hkdf.ts:10-11) β€” prevents CEK/commit-key confusion
  4. No user-supplied nonces in the public API (src/primitives/aead.ts:68) β€” nonce reuse is architecturally impossible
  5. Constant-time comparison via timingSafeEqual on Node, XOR-accumulate fallback on browser (src/internal/constant-time.ts)
  6. Secure memory with sodium_malloc/mlock and explicit insecureMemory acknowledgement for browsers
  7. PBKDF2 iteration floor of 1,000,000 (src/passphrase.ts:60) β€” above OWASP minimums
  8. Exhaustive key zeroing in finally blocks (src/envelope/rewrap.ts:192-201)
  9. RFC 8785 canonicalization with surrogate-pair validation (src/canonical-json.ts:115-126)
  10. Provenance-signed npm publishing via OIDC Trusted Publishing (publish.yml:58)

Summary Table

# Severity File Finding
M-1 Medium .github/workflows/dependabot-handler.yml:13,25 pull_request_target + secrets: inherit pattern
M-2 Medium src/message-counter.ts:41 / src/envelope-client.ts:140 In-memory counter default allows AES-GCM nonce budget bypass on restart
M-3 Medium .github/workflows/security-review.yml:52 Unpinned @anthropic-ai/claude-code install in CI
L-1 Low src/envelope/v1.ts:154 JSON.parse of decrypted data β€” consumer-side prototype pollution risk
L-2 Low package.json:49 esbuild dev dependency has known high-severity CVE
L-3 Low .github/workflows/claude-agent.yml:67 Empty allowed_non_write_users β€” informational
L-4 Low .github/workflows/ci.yml:52 Inconsistent actions/checkout@v4 vs @v6

Overall assessment: The cryptographic core is well-implemented with no identified vulnerabilities. Findings are limited to CI/CD supply-chain hygiene (Medium) and informational best-practice items (Low). The library's security architecture β€” CSPRNG nonces, AAD binding, key commitment, verify-after-encrypt, constant-time operations, secure memory β€” is sound and consistent with its documented threat model.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions