init renders workflow names, check commands and the default branch into YAML/JSON/regex without escaping for that context
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 71
- Forks
- 64
- Avg merge
- 15h 38m
- Merged PRs (30d)
- 66
Description
Summary
render.mjs substitutes {{VARS}} verbatim, which is the right primitive, but three consumers put user- or repo-controlled strings into a structured context (YAML, JSON, a regex literal) without escaping for that context. Each one produces a file that is silently invalid. #217 is the same shape for {{PROVISION_CMD}}; these are the remaining ones I could reproduce.
1. DOCTOR_WATCH: detected workflow names rendered as bare YAML scalars
src/detect.mjs:139 deliberately strips the quotes from name: "...", and src/init.mjs:103 writes the result back raw under on.workflow_run.workflows in facility-doctor.yml. A workflow named CI: Build & Test renders as - CI: Build & Test (a mapping — GitHub rejects it); [nightly] e2e fails to parse outright. Either way the doctor workflow never fires, and workflow_run errors are only visible in the Actions tab.
Fix: JSON.stringify(name) in doctorWatch.
2. checksAllowJson: check commands spliced into .claude/settings.json without JSON escaping
src/init.mjs:113: ` "Bash(${c})",`. A check like node -e "process.exit(0)" produces invalid JSON (Expected ',' or ']' … line 7), and Claude Code then ignores the whole settings file — permissions and hooks.
Fix: JSON.stringify(Bash(${c})).
3. DEFAULT_BRANCH spliced into a regex literal in protect-branch.mjs
templates/claude/hooks/protect-branch.mjs:10: const PROTECTED = /^(origin\/)?({{DEFAULT_BRANCH}}|main|master)$/;. With --branch=release/2026 the rendered file is
const PROTECTED = /^(origin\/)?(release/2026|main|master)$/;
// SyntaxError: Invalid regular expression flags
The hook now exits 1 on every Bash call (Claude Code logs the error and lets the command through), so exactly the repositories with non-trivial branch names lose the force-push/direct-push guard. ., +, ( in a branch name misbehave more quietly.
Fix: render the branch with JSON.stringify and build the regex with new RegExp over an escaped string.
Test gap
CONTRIBUTING.md says a change to generated YAML must be covered by a test that parses and exercises the rendered workflow, but packages/cli/test/init.test.mjs only JSON.parses manifests/settings and string-matches the workflows; nothing in the CLI tests parses YAML (grep -rn yaml packages/cli/test is empty). A single test that renders init with a "hostile but ordinary" set of inputs (workflow name with : , branch with /, check with ") and parses every generated YAML/JSON/JS file would have caught all of these plus #217.
Environment: facility main, Node 24, Windows 11; reproduced by running bin/facility.mjs init into temp repositories.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/detect.mjs, src/init.mjs, templates/claude/hooks/protect-branch.mjs, and packages/cli/test/init.test.mjs; run the existing init tests and reproduce the hostile inputs described in the issue. Done means generated YAML, JSON, and JavaScript remain valid for workflow names, check commands, and branch names, with tests parsing the rendered files.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, testing-qa, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100