# Risk map — Chalarangelo/30-seconds-of-code

> Canonical page: https://codevetter.com/unpack/30-seconds-of-code/risk-map

# Risk map — Chalarangelo/30-seconds-of-code

Scanned commit bd756f8b2283a38560efe83cb30af38324d4e9e9 (2026-09-09). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Top risks: production code imports test factories (spec/ is on the live path of bin/console and any Loader user); the model layer is global mutable state; the markdown pipeline passes through raw HTML (allowDangerousHtml) so content is trusted input; watch mode has no debounce; omnisearch injects index data into innerHTML; and several identifiers are derived by brittle string slicing/naming conventions. Deploy security hinges on a single secret webhook URL, and CI is thin (tests only, old action versions, no build check).
- src/lib/loader.js imports '#spec/factories/factories.js' — test-only factory code is imported by a production module, coupling any Loader user (bin/prepare, bin/console) to the spec tree. — [src/lib/loader.js#L18](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L18) [spec/factories/factories.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/factories/factories.js)
- Model state is process-global and append-only: Model.instances accumulates every constructed instance with no reset API, so double importData calls or tests that create factories leak records into all/where/scope results for the process lifetime. — [src/core/model.js#L4-7](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L4-L7) [src/core/model.js#L52-63](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L52-L63) [src/lib/loader.js#L43-54](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L43-L54)
- The markdown pipeline runs remark-rehype and rehype-stringify with allowDangerousHtml:true — raw HTML embedded in snippet markdown passes through unescaped into shipped pages; safe only because all content is author-trusted. — [src/lib/contentUtils/markdownParser/markdownParser.js#L86](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L86) [src/lib/contentUtils/markdownParser/markdownParser.js#L95](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L95)
- omnisearch renders result rows via innerHTML template literals interpolating result.url, result.title, result.tag without escaping (createResultHTML), while only the not-found path escapes the query — a crafted search-data.json title could inject markup, though the index is build-generated. — [src/astro/scripts/omnisearch.js#L94-117](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L94-L117)
- Watch mode exec()s a full 'bin/prepare dev' on every fs.watch event with no debounce or in-flight guard — rapid saves can spawn overlapping full re-extractions (Shiki highlighting is the expensive step). — [bin/prepare#L91-99](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L91-L99) [src/lib/contentUtils/fileWatcher.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/fileWatcher.js)
- Snippet id derivation is positional: filePath.replace(snippetPrefix,'').slice(0,-3) assumes the 'content/snippets/…/*.md' shape; a stray file matched by the glob with a different extension would silently corrupt the id. — [src/lib/contentUtils/modelWorkers/snippet.js#L29](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L29) [src/lib/contentUtils/config.js#L6](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js#L6)
- A missing/typo'd front-matter 'language' degrades silently: languageData.get(languageKey) returns undefined and every downstream use is optional-chained, so recTokens/docTokens lose language terms and the snippet renders without a language rather than failing. — [src/lib/contentUtils/modelWorkers/snippet.js#L28-46](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L28-L46)
- Naming-convention coupling is load-bearing: Page.from resolves `${object.constructor.name}Page`, serializeAs resolves `${name}Serializer`, and the JSON reviver resolves models[value.model] — renaming a class or key breaks page generation, and the 'serializer not found' guard in serializeAs is dead code because `new undefined()` throws TypeError first. — [src/adapters/page.js#L10-13](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L10-L13) [src/models/contentModel.js#L118-127](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L118-L127) [src/lib/loader.js#L47-50](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L47-L50)
- Publishing correctness depends on wall-clock time: dateModified compared to new Date() decides scheduled vs published, so the daily 18:20 UTC deploy cron determines when scheduled articles go live — a missed/failed deploy silently delays publication. (inferred) — [src/models/snippet.js#L180-190](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L180-L190) [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [src/lib/astroContent.js#L45-49](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js#L45-L49)
- Astro routes hard-fail without generated content: getStaticPaths does fs.readFile on .content JSONs with no fallback, so 'astro dev'/'astro build' without the prepare step (or a cleaned .content dir) crashes — the coupling is enforced only by npm pre-hooks, which bare 'astro' or 'vitest' invocations bypass. — [src/astro/pages/[lang]/s/[snippet].astro#L15-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/s/%5Bsnippet%5D.astro#L15-L26) [src/astro/pages/index.astro#L13-14](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/index.astro#L13-L14) [package.json#L44-51](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L44-L51)
- Known-fragile code is flagged in TODO comments: Snippet.hasCollection returns true even when the only membership is the main collection, and Collection.matchesTag relies on id.endsWith(`/${tag}`). — [src/models/snippet.js#L212-217](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L212-L217) [src/models/collection.js#L160-163](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/collection.js#L160-L163)
- CI uses dated action versions (actions/checkout@v1 in deploy-production, actions/setup-node@v1 in test) and the deploy job's only secret is CF_BUILD_HOOK_URL — a leaked or misconfigured hook URL is the entire deployment control surface. — [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [.github/workflows/test.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/test.yml) [bin/deploy](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/deploy)
- Shiki vs Prism output differs subtly: Prism mode appends a '<script type=module …/prism-code-highlights.mjs>' tag into parsed HTML, meaning dev-mode rendered markup is not identical to production markup. — [src/lib/contentUtils/markdownParser/markdownParser.js#L113-118](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L113-L118) [bin/prepare#L69-74](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L69-L74)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
