Files
my-pi/pi-permission-system/docs/plans/archive/0073-wildcard-dotall-multiline.md

5.2 KiB

issue, issue_title
issue issue_title
73 node -e command triggers permission prompt despite "*": "allow" global fallback

Wildcard matcher fails on multiline bash commands

Problem Statement

When a bash command contains newlines (e.g., node -e "\nimport(...)\n"), the wildcard matcher's compiled regex /^.*$/ fails because . does not match \n by default in JavaScript. This causes evaluate() to find no matching rule — not even the universal { surface: "*", pattern: "*" } catch-all — and fall through to the hard-coded "ask" default, producing a spurious permission prompt.

Goals

  • Make wildcardMatch (and all callers via compileWildcardPattern) correctly match values containing newline characters.
  • Zero behavior change for single-line values — this is a strict bug fix.

Non-Goals

  • Changing the permission evaluation model or rule precedence.
  • Addressing shell-quote tokenization (covered by #72, already landed).
  • Adding multiline-aware pattern syntax (e.g., ** for newlines) — the existing * should simply match everything including newlines.

Background

  • Permission surface: bash (but the fix is in the generic wildcard module, affecting all surfaces).
  • Module: src/wildcard-matcher.tscompileWildcardPattern() builds a RegExp by escaping literal segments and joining with .*. The regex uses no flags, so . does not match \n.
  • Call path: evaluate() in src/rule.ts calls wildcardMatch(rule.pattern, value) for both surface and pattern fields. A multiline bash command is the value argument to the pattern match.

Design Overview

Add the s (dotAll) flag to the regex constructed in compileWildcardPattern. With dotAll, . matches any character including line terminators (\n, \r, \u2028, \u2029).

return {
  pattern,
  state,
  regex: new RegExp(`^${escaped}$`, "s"),
};

This is the minimal, correct fix. No new types, no data-shape changes, no config changes.

Module-Level Changes

File Change
src/wildcard-matcher.ts Add "s" flag to new RegExp(...) in compileWildcardPattern().
tests/wildcard-matcher.test.ts Add test cases for multiline values matching * and literal-prefix-* patterns.
tests/permission-manager.test.ts Add integration test: multiline bash command with "*": "allow" returns allow.

TDD Order

  1. test: multiline value fails to match wildcard * pattern (red) Add tests in tests/wildcard-matcher.test.ts:

    • wildcardMatch("*", "line1\nline2") → expected true
    • wildcardMatch("node *", "node -e \"\nfoo\n\"") → expected true
    • compileWildcardPattern("*", "allow").regex.test("a\nb") → expected true

    Commit: test: cover multiline values in wildcardMatch

  2. feat: add dotAll flag to wildcard regex In src/wildcard-matcher.ts, change new RegExp(...) to include "s" flag. Tests from step 1 go green.

    Commit: fix: add dotAll flag so wildcard*matches newlines (#73)

  3. test: integration — multiline bash command resolves to allow Add a test in tests/permission-manager.test.ts:

    • Config: { "*": "allow", bash: { "rm -rf *": "deny" } }
    • checkPermission("bash", { command: "node -e \"\nimport(...)\n\"" })state: "allow"

    Commit: test: multiline bash command resolves allow via universal fallback

Risks and Mitigations

Risk Mitigation
Could this silently weaken a permission? No. The s flag only makes * match what users already expect it to match. A pattern like rm -rf * still only matches strings starting with rm -rf — it does not gain the ability to match unrelated multiline strings because the literal prefix anchors it.
Over-broad match for patterns containing literal newlines No user would put literal \n in a pattern string in JSON config. The patterns are single-line strings; only values (commands) can be multiline.
Breaks existing tests Existing tests use single-line values. Adding s has no effect on strings without newlines.

Open Questions

None — the fix is unambiguous.