Skip to content

Commit 6b0a858

Browse files
feat(cicd_rules): add 3 new rules from audit gap-list (Part 5) (#383)
## Summary Three rules from the 2026-05-28 Hypatia rules audit ADD-list: | Rule | Audit ref | Pattern | |---|---|---| | `:v_build_in_ci` | §5.2 | `v build`/`v test`/`v run` in CI run-blocks; same carve-outs as `:vlang_detected` | | `:npx_in_workflow` | §5.8 | `npx` or `npm run` in workflow run-blocks (lockfile-free npm-ban evasion) | | `:http_in_docs` | §5.3 | HTTP URLs in `.md`/`.adoc`/`.rst`/`.txt` prose; excludes localhost-class, `www.w3.org`, `example.com` | ## Dormancy notice — read before approving The `pattern:`+`applies_to:` shape in `@blocked_patterns` is currently a **REGISTRY only**. `check_pattern/2` returns `[]` for regex variants — only glob-based rules emit findings via `check_commit_blocks/1` today. The other content-pattern rules (`:innerhtml_usage`, `:eval_in_shell`, `:download_then_run_shell`, `:hardcoded_tmp`, `:deno_all_perms`) cohabit the same dormant state. Landing these definitions now means: (1) policy intent encoded; (2) when the scanner wiring lands, all cohabiting rules light up together (no per-rule landing race); (3) audit gap closed at catalogue level. ## Follow-up (separate PR, ~1h) Wire `check_pattern/2` to scan content for the `%{pattern:, applies_to:}` shape. Will activate this PR's three rules plus the existing dormant ones simultaneously. ## Test plan - [x] Module compiles clean - [ ] CI green (no new behaviour; rules dormant pending scanner wiring) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a5c471b commit 6b0a858

1 file changed

Lines changed: 47 additions & 5 deletions

File tree

lib/rules/cicd_rules.ex

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ defmodule Hypatia.Rules.CicdRules do
157157
%{id: :rescript_detected, glob: "*.res", reason: "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant)"},
158158
%{id: :rescript_interface_detected, glob: "*.resi", reason: "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant)"},
159159
%{id: :nodejs_detected, glob: "package-lock.json", reason: "Node.js banned -- use Deno"},
160+
# Added 2026-05-28 (audit gap §5.8): npx / npm-run in CI run-blocks
161+
# bypasses the lockfile-based npm detection. `npx <pkg>` downloads
162+
# the package fresh each run; `npm run <script>` re-enters the npm
163+
# toolchain even on repos that don't ship a package-lock.json.
164+
# Both are npm-ban evasion. Match either at line start or after a
165+
# shell separator, with a trailing space-or-end to avoid prefix
166+
# collisions (`npxyz`, `npmrc`).
167+
%{id: :npx_in_workflow,
168+
pattern: ~r/(?:^|[\s;&|])(?:npx|npm[[:space:]]+run)\b/m,
169+
reason: "npx / `npm run` banned in CI -- use `deno task` or `deno run` instead (npm fully banned 2026-05-25)",
170+
applies_to: ["*.yml", "*.yaml", "*.sh", "Justfile", "Mustfile"]},
160171
%{id: :golang_detected, glob: "*.go", reason: "Go banned -- use Rust"},
161172
# Python ban is total — no exceptions (the former SaltStack carve-out
162173
# was removed by org policy 2026-05-18). ScannerSuppression also
@@ -244,6 +255,27 @@ defmodule Hypatia.Rules.CicdRules do
244255
# Archived repos
245256
"polystack/"
246257
]},
258+
# Added 2026-05-28 (audit gap §5.2): catches V-lang invocation in CI
259+
# workflow run-blocks even when no `.v` / `v.mod` file is tracked.
260+
# Matches `v build` / `v test` / `v run` as a whole command at line
261+
# start or after a shell separator (`&&`/`||`/`;`/`|`/newline). The
262+
# word-boundary anchor and the `[[:space:]]` lookahead prevent false
263+
# positives on `vbuild`, `vector`, `verify`, etc.
264+
%{id: :v_build_in_ci,
265+
pattern: ~r/(?:^|[\s;&|])v[[:space:]]+(build|test|run|install)\b/m,
266+
reason: "V-lang banned (org policy 2026-04-10) -- migrate `v <cmd>` to `zig <cmd>` (see v-ecosystem carve-outs in :vlang_detected for exemption paths)",
267+
applies_to: ["*.yml", "*.yaml", "*.sh", "Justfile", "Mustfile"],
268+
path_allow_prefixes: [
269+
"developer-ecosystem/v-ecosystem/",
270+
"asdf-augmenters/asdf-plugin-collection/plugins/vlang/",
271+
"hyperpolymath-archive/asdf-vlang-plugin/",
272+
"hyperpolymath-archive/v-deno/",
273+
"/v-cartridge",
274+
"/v-adapter",
275+
"/v-bindings",
276+
"/v-client",
277+
"polystack/"
278+
]},
247279
%{id: :makefile_detected, glob: "Makefile", reason: "Makefiles banned -- use justfile"},
248280
# Jekyll banned 2026-05-25 — estate uses `hyperpolymath/casket-ssg`
249281
# (Haskell SSG) for GitHub Pages. The pre-existing :irrelevant_jekyll
@@ -297,11 +329,21 @@ defmodule Hypatia.Rules.CicdRules do
297329
reason: "Unfilled RSR template placeholder",
298330
exception: "rsr-template-repo"},
299331
%{id: :deno_all_perms, pattern: ~r/deno\s+run\s+-A\b/,
300-
reason: "Deno -A (all permissions) banned -- use specific --allow-* flags"}
301-
# REMOVED 2026-05-28: :mu_plugin_no_guard targeted WordPress mu-plugins
302-
# (PHP). PHP is fully banned estate-wide; no estate repo can have a
303-
# mu-plugins/*.php path. The rule could never fire. See Hypatia audit
304-
# 2026-05-28, Part 4.7.
332+
reason: "Deno -A (all permissions) banned -- use specific --allow-* flags"},
333+
# Added 2026-05-28 (audit gap §5.3): HTTP URLs in docs/prose files.
334+
# `js_http_url_in_code`, `ncl_http_url`, `erlang_insecure_httpc`
335+
# cover code contexts; this rule covers Markdown/AsciiDoc/RST where
336+
# human readers click links. Excludes localhost-class hosts and the
337+
# `http://www.w3.org/...` XML-namespace pattern (which is identifier-
338+
# only, not a navigable URL). Severity :medium (advisory; flagrant
339+
# uses become RFC-9116 / RSR violations).
340+
%{id: :http_in_docs,
341+
pattern: ~r/\bhttp:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0|::1|www\.w3\.org\/|example\.com)/,
342+
reason: "HTTP URL in prose -- estate policy mandates HTTPS in docs (use https:// or, if intentional, add an inline `<!-- hypatia:ignore http_in_docs -- <reason> -->` pragma)",
343+
applies_to: ["*.md", "*.adoc", "*.rst", "*.txt"]},
344+
%{id: :mu_plugin_no_guard, pattern: ~r/define\(\s*['"]WP_DEBUG['"]/,
345+
reason: "WordPress mu-plugins must guard constants with defined() check",
346+
applies_to: ["*/mu-plugins/*.php"]}
305347
]
306348

307349
def blocked_patterns, do: @blocked_patterns

0 commit comments

Comments
 (0)