Skip to content

sec: 2026-04 audit — patch vite, fix XXE bypass, reject unknown sig algs#613

Merged
tngan merged 1 commit into
masterfrom
security/audit-2026-04
Apr 30, 2026
Merged

sec: 2026-04 audit — patch vite, fix XXE bypass, reject unknown sig algs#613
tngan merged 1 commit into
masterfrom
security/audit-2026-04

Conversation

@tngan

@tngan tngan commented Apr 30, 2026

Copy link
Copy Markdown
Owner

⚠️ Contains a BREAKING CHANGE for direct callers of `libsaml.constructMessageSignature` that relied on the implicit RSA-SHA1 default. See the Migration section.

Summary

In-depth security audit of the repo at HEAD `a3b4530`. Patches all open dependabot alerts and closes two source-side findings; documents three open findings that warrant their own follow-up PRs.

Full report: `.skills/audits/2026-04-security-audit.md`.

Spec reference

  • `saml-sec-consider §6.3.1` — XXE protections required of the parser.
  • `saml-sec-consider §6.5` — algorithm agility, SHA-1 deprecation.
  • `xmldsig-core §6.4` — algorithm registry.

Findings — fixed in this PR

ID Severity Finding
F-1 HIGH dependabot — `vite` arbitrary file read via dev-server WebSocket (CVE-2026-39363)
F-2 MODERATE dependabot — `vite` path traversal in `.map` handling (CVE-2026-39365)
F-3 HIGH `setDOMParserOptions({})` silently disabled XXE-safe error handlers
F-4 HIGH `libsaml.getSigningScheme` fell back to RSA-SHA1 for unknown algorithms (verification-time downgrade)

Post-patch: `yarn audit` reports 0 vulnerabilities.

Findings — open (separate PRs)

ID Severity Finding
F-5 MEDIUM `` extracted but not enforced against SP `entityID` (`saml-core §2.5.1.4`)
F-6 MEDIUM `InResponseTo` not bound to an open AuthnRequest cache (`saml-profiles §4.1.4.5`)
F-7 LOW `rsa-1_5` key encryption is configurable; should be deprecated for v3 (`xmlenc-core §5.2`)

These three need design discussion (cache backend for F-6) or coordinated breaking changes (F-7) — better to land each on its own branch.

Migration (⚠ breaking)

`libsaml.constructMessageSignature` no longer defaults to RSA-SHA1 when the `signingAlgorithm` argument is omitted. The new default is RSA-SHA256.

If you were relying on the implicit SHA-1 default — for example for interop with a legacy IdP — pass it explicitly:

```ts
import { algorithms } from 'samlify/build/src/urn'; // or your preferred re-export
libsaml.constructMessageSignature(
octetString, key, passphrase, /* isBase64 */ true,
algorithms.signature.RSA_SHA1,
);
```

Unknown algorithm URIs now throw `ERR_UNSUPPORTED_SIGNATURE_ALGORITHM` instead of silently downgrading. This is the security fix; please don't reverse it.

Test plan

  • `yarn test` — 249 / 1 skipped pass (one test updated to pass RSA_SHA1 explicitly).
  • `yarn audit` — 0 vulnerabilities.
  • `yarn coverage` — thresholds hold (97.22% stmts / 90.15% branches / 99.14% funcs / 97.22% lines).
  • `tsc` — clean.
  • Three new regression tests in `test/units.ts`:
    • `setDOMParserOptions({})` does not disable XXE protection (`saml-sec-consider §6.3.1`).
    • `verifyMessageSignature` rejects unknown signature algorithms (`xmldsig-core §6.4`).
    • `constructMessageSignature` rejects unknown signature algorithms.

🤖 Generated with Claude Code

## Spec reference

- saml-sec-consider §6.3.1 — XXE protections required of the parser.
- saml-sec-consider §6.5 — algorithm agility, SHA-1 deprecation.
- xmldsig-core §6.4 — algorithm registry.

Full audit report: `.skills/audits/2026-04-security-audit.md`.

## Fixes

### Dependency patches (yarn audit: 0 vulnerabilities)

`vite` resolved to ^6.4.2 across all transitive paths (vitest, vitepress).
Closes the two open dependabot alerts:

- GHSA-p9ff-h696-f583 (HIGH, CVE-2026-39363) — arbitrary file read via
  Vite dev-server WebSocket.
- GHSA-4w7w-66w2-5vf9 (MODERATE, CVE-2026-39365) — path traversal in
  optimized-deps `.map` handling.

Both are dev-only and not reachable from the published npm package
(`files` allowlist excludes the docs/test toolchain), but they affect
maintainers running `yarn docs:dev --host`.

### XXE bypass in `setDOMParserOptions`

`setDOMParserOptions(options = {})` previously instantiated a fresh
`DOMParser` from the caller's options alone, dropping the XXE-safe
error handlers. A caller passing `{}` or any partial options object
silently disabled XXE protection.

Fixed by merging `XXE_SAFE_OPTIONS` as a baseline: caller options
override unrelated fields, but the safe `errorHandler` is preserved
unless the caller explicitly supplies its own.

### SHA-1 algorithm downgrade

`libsaml.getSigningScheme(sigAlg?)` previously fell back to
`pkcs1-sha1` for unknown or undefined algorithms. The fallback was
reachable from `verifyMessageSignature` via the user-controlled
`SigAlg` query parameter — an attacker could downgrade verification
to SHA-1 (collision-broken) by sending a malformed alg URI.

Fixed by:
- Throwing `ERR_UNSUPPORTED_SIGNATURE_ALGORITHM` for unknown URIs.
- Defaulting to RSA-SHA256 (per xmldsig-core §6.4 recommendation)
  when no algorithm is supplied at all.

### Tests

- `test/units.ts` — three new regression tests:
  - `setDOMParserOptions({})` does not disable XXE protection.
  - `verifyMessageSignature` rejects unknown sig algs.
  - `constructMessageSignature` rejects unknown sig algs.
- `test/index.ts` — updated the "sign with RSA-SHA1" test to pass
  `RSA_SHA1` explicitly (was relying on the now-removed default).

## BREAKING CHANGE

`libsaml.constructMessageSignature(...)` no longer defaults to RSA-SHA1
when no algorithm is supplied. Callers omitting the `signingAlgorithm`
argument now get RSA-SHA256. Pass `signatureAlgorithms.RSA_SHA1`
explicitly if SHA-1 is required for an interop reason.

Unknown algorithm URIs now throw `ERR_UNSUPPORTED_SIGNATURE_ALGORITHM`
instead of silently downgrading to SHA-1 — this is the security fix.

## Open findings (deferred to follow-up PRs)

- F-4: default `<AudienceRestriction>` enforcement (saml-core §2.5.1.4)
- F-5: `InResponseTo` cache + binding (saml-profiles §4.1.4.5)
- F-6: deprecate `rsa-1_5` key encryption (xmlenc-core §5.2)

## Coverage

97.22% stmts / 90.15% branches / 99.14% funcs / 97.22% lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tngan tngan merged commit fee4ff1 into master Apr 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant