Skip to content

fix(security): prevent deep-nesting stack overflow, harden API and CI#65

Merged
P4suta merged 1 commit into
mainfrom
security/pre-release-hardening
Jun 13, 2026
Merged

fix(security): prevent deep-nesting stack overflow, harden API and CI#65
P4suta merged 1 commit into
mainfrom
security/pre-release-hardening

Conversation

@P4suta

@P4suta P4suta commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Pre-public-release security hardening

A focused security pass ahead of public release. comrak stays verbatim (0-line diff); verified green via just ci.

Fixes

  • Deep-nesting stack overflow → DoS (F1, the headline): the recursive comrak-AST walks (ast_splice::walk, sentinel_stream::visit_text_leaves) are replaced with explicit work-stack iteration, and the IR builder is depth-bounded. comrak caps list nesting at 100 but not blockquotes, so a small input like many > markers built an arbitrarily deep AST that overflowed the call stack — under panic = "abort" a hard process abort, fatal for any server-side library embedder. Both repos' SECURITY.md scope this (a crash on untrusted input) in as a vulnerability. New tests/deep_nesting.rs pins ~100k-deep input returning instead of crashing, with Tier-A preserved.
  • XSS footgun (F3): the raw-HTML spec-only Options constructors (commonmark_only / gfm_only, which enable render.unsafe) are hidden from the published surface and carry a prominent warning. Production afm_default is unaffected (raw HTML stays escaped).
  • Boundary guard (A2): the public entry points reject input larger than u32::MAX before the core lexer would abort under panic = "abort".

Hardening

  • Supply chain / CI: a per-PR audit-comrak gate matches the vendored comrak version against RustSec advisories the path dep otherwise hides; Docker base images pinned by digest; composite-action deps pinned by SHA.
  • Playground: strict production Content-Security-Policy.

Verification

just ci green locally (19 steps: lint, build-and-test, spec ×2, coverage, audit ×3, upstream-diff, types-check, wasm, book, …).

🤖 Generated with Claude Code

Pre-public-release security hardening (comrak stays verbatim, 0-line diff):

- splice/ir: replace the recursive comrak-AST walks (ast_splice::walk,
  sentinel_stream::visit_text_leaves) with explicit work-stack
  iteration, and depth-bound the IR builder. Deeply nested input (e.g.
  many `> ` blockquotes — comrak caps list nesting at 100 but not
  blockquotes) no longer overflows the stack and aborts the process
  under panic=abort. Adds tests/deep_nesting.rs regression coverage.
- api: hide the raw-HTML spec-only Options constructors (commonmark_only
  / gfm_only) from the published surface and warn; add an oversized-
  input guard to the public entry points.
- ci/supply-chain: add a per-PR audit-comrak gate matching the vendored
  comrak version against RustSec advisories; pin Docker base images by
  digest and composite-action deps by SHA; document the panic=abort
  embedder contract in SECURITY.md.
- playground: add a strict production Content-Security-Policy.

Verified green via `just ci`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@P4suta P4suta enabled auto-merge (squash) June 13, 2026 16:18
@github-actions

Copy link
Copy Markdown

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ❌ 1 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
See the Details below.

License Issues

.github/workflows/release.yml

PackageVersionLicenseIssue Type
Swatinem/rust-cachee18b497796c12c097a38f9edb9d0641fb99eee32LGPL-3.0Incompatible License
Allowed Licenses: Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause, ISC, Unicode-DFS-2016, Unicode-3.0, CC0-1.0, MPL-2.0, CC-BY-SA-4.0

OpenSSF Scorecard

PackageVersionScoreDetails
actions/Swatinem/rust-cache e18b497796c12c097a38f9edb9d0641fb99eee32 🟢 6.2
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review🟢 3Found 5/14 approved changesets -- score normalized to 3
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Pinned-Dependencies🟢 9dependency not pinned by hash detected -- score normalized to 9
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 7SAST tool is not run on all commits -- score normalized to 7
actions/actions/attest-build-provenance e8998f949152b193b063cb0ec769d69d929409be UnknownUnknown
actions/actions/checkout df4cb1c069e1874edd31b4311f1884172cec0e10 🟢 5.9
Details
CheckScoreReason
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review🟢 10all changesets reviewed
Maintained⚠️ 23 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Packaging⚠️ -1packaging workflow not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 8SAST tool detected but not run on all commits
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
actions/actions/download-artifact 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c 🟢 5.3
Details
CheckScoreReason
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review🟢 10all changesets reviewed
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 23 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
actions/actions/upload-artifact 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a 🟢 5.5
Details
CheckScoreReason
Code-Review🟢 8Found 8/9 approved changesets -- score normalized to 8
Maintained🟢 54 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 5
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
actions/orhun/git-cliff-action f50e11560dce63f7c33227798f90b924471a88b5 🟢 4.1
Details
CheckScoreReason
Code-Review🟢 5Found 15/28 approved changesets -- score normalized to 5
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 56 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 5
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Pinned-Dependencies🟢 5dependency not pinned by hash detected -- score normalized to 5
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
License🟢 10license file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
actions/softprops/action-gh-release b4309332981a82ec1c5618f44dd2e27cc8bfbfda 🟢 5.5
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 0Found 1/16 approved changesets -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Pinned-Dependencies🟢 10all dependencies are pinned
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Security-Policy⚠️ 0security policy file not detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • .github/workflows/release.yml

@P4suta P4suta merged commit 639f4dd into main Jun 13, 2026
23 of 24 checks passed
@P4suta P4suta deleted the security/pre-release-hardening branch June 13, 2026 16:22
P4suta added a commit that referenced this pull request Jun 14, 2026
Pre-1.0 minor bump **0.3.0 → 0.4.0** to cut the next afm release,
tracking the freshly released **aozora v0.4.0**.

Changes (release bookkeeping only — no afm source changes):
- bump afm workspace version 0.3.0 → 0.4.0
- sync the umbrella `aozora` pin (and the cargo-fuzz pin) to the
  released aozora **v0.4.0** commit `df0f64b` (tagged + provenance-
  attested), and bump the `version = "0.4.0"` constraint to match
- promote `CHANGELOG.md` `[Unreleased]` → `[0.4.0] - 2026-06-14`

⚠️ **Reviewer note — CHANGELOG completeness:** the `[Unreleased]`
section is hand-curated. I promoted the heading and preserved all
existing content, but did **not** backfill the most recent commits
(#57#67: later playground features, `fix(security)` deep-nesting
stack-overflow #65, build provenance attestation #64/#66). Add any
user-facing entries you want under `[0.4.0]` before merge.

`just check` passes against aozora 0.4.0. After merge: tag `v0.4.0`
on main → `release.yml` builds the three target binaries + attests
build provenance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
P4suta added a commit that referenced this pull request Jun 14, 2026
Post-v0.4.0 maintenance, mirroring the sibling aozora repo and
addressing warnings from the v0.4.0 release run.

**CI (`ci:` commit)**
- bump `actions/attest-build-provenance` `v2.4.0` → `v4.1.0`
(SHA-pinned): v2.4.0 runs on the deprecated Node 20; v3+ moved to Node
24. `subject-path` input unchanged.
- pin the Windows release runner `windows-latest` → `windows-2025`.
- add `CARGO_NET_RETRY: "10"` to `x-common-env` to ride out transient
`static.crates.io` blips.

**Docs (`docs:` commit)**
- backfill the hand-curated `[0.4.0]` CHANGELOG section, which lagged
the latest commits: round-2 playground features (#57–62), build
provenance attestation (#64, #66), the aozora v0.4.0 pin, and the
deep-nesting stack-overflow fix (#65).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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