fix(ea1): reject footnote-legend '*', bound pre-colon gap, detect YAML block-list and JSON wildcard grants - #447
fix(ea1): reject footnote-legend '*', bound pre-colon gap, detect YAML block-list and JSON wildcard grants#447yashrajp22 wants to merge 14 commits into
Conversation
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Requesting changes: the new same-line list branch introduces an EA1 false positive when an explicit tool object has a nested argument value of "*"; the wildcard must be a top-level tool-list element. The focused suite passes, but this head has no hosted checks, and the feature commit also lacks the required DCO sign-off. Please add the nested-object regression, narrow the match, and rerun signed CI.
| # stays on one line so the match can never bridge paragraphs (#405, #444). | ||
| ( | ||
| r"['\"]?(?:tools?|permissions?)['\"]?[ \t]*:[ \t]*" | ||
| r"(?:\[[^\]\r\n]*['\"]\*['\"]" |
There was a problem hiding this comment.
[P1] Restrict the wildcard to a top-level tool-list element. The unrestricted [^\]\r\n]* scan treats any nested quoted star as the wildcard tool itself: valid JSON such as "tools": [{"name": "search", "arguments": {"glob": "*"}}] now emits EA1 even though the tool list explicitly contains only search. Match scalar list elements without descending into nested objects/arrays, and add this JSON case as a negative regression.
…ck-list and JSON wildcard grants Fixes #444. Fixes #445. Follow-up to #405/#417. Remaining false positives (#444): - A bare '*' now counts only when it ends the line (optionally ']' and/or a '#' comment), so footnote legends like 'Tools: * = requires auth' no longer fire. Quoted '*' stays unambiguous as a scalar or top-level list element. - The gap before the colon is bounded to the same line ([ \t]*:), so a blank line followed by a markdown definition-list ': *' can no longer bridge paragraphs. Detection gaps (#445): - The key may be quoted, catching JSON forms: "tools": ["*"] and "permissions": "*". - New block-list branch catches the idiomatic YAML form (tools: newline '- "*"'), bounded to a single newline with a standalone-star item so markdown lists of bold/italic names cannot collide. - A quoted '*' as a later top-level element of a same-line list now matches (tools: ["search", "*"]). The list branch excludes braces so a '*' nested inside an explicit tool object (tools: [{name: grep, pattern: "*"}]) is treated as an argument value for a named tool, not a wildcard grant. Validated against a 49-case matrix (20 genuine grant forms, 11 new detections, 18 false-positive classes); full suite 2966 passed, ruff check/format clean. Signed-off-by: yashrajbasav <[email protected]>
6a2a7d4 to
a6b1d91
Compare
|
All three points addressed in the rewritten head ( 1. Nested-object false positive — fixed and regression-tested. The same-line list branch now excludes
One documented non-detection this implies: a top-level 2. DCO — the branch was rewritten with 3. CI — the branch is rebased onto current |
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Re-review: the rewritten head is signed, rebased directly onto current main, and all hosted checks are green. It fixes the reported nested-object case, and the focused and relevant static-pattern suites pass. However, the same list regex still descends into nested arrays because [ remains allowed in its scan: tools: ["search", ["grep", "*"]] still emits EA1/MEDIUM even though * is not a top-level tool-list element. Please prevent traversal into nested arrays and add this negative regression; the existing review thread remains open.
The same-line list branch excluded '{'/'}' but still allowed '[', so a
quoted '*' inside a nested array (tools: ["search", ["grep", "*"]])
was treated as a top-level tool-list element. Exclude '[' from the scan
as well and document the quoted-delimiter ceiling. Adds the nested-array
and nested-arguments-object negative regressions from review.
Signed-off-by: yashrajbasav <[email protected]>
|
Fixed in
Local on the rebased head: 3,995 passed / 0 failed ( |
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Reviewed current head 375eafe12a76f073efbc1167319e7bb8636c7a59. The prior nested-object and nested-array EA1 false positives are both resolved by the current delimiter handling, with regressions for each shape. I found no remaining required code or test change.
This head was refreshed by the bot during review and has no hosted check results yet. The PR is currently blocked, and the outdated review thread should be resolved before merging.
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Corrective re-review of current head 3f6f63a3cd1d104dcb35fd1d7858d86461aaaacf. A required EA1 correctness issue remains: the inline-list branch treats a quoted wildcard embedded inside a named tool string as a top-level unrestricted-tool grant. Please make the matcher element/quote-aware and add the negative regression described inline.
| # can never bridge paragraphs (#405, #444). | ||
| ( | ||
| r"['\"]?(?:tools?|permissions?)['\"]?[ \t]*:[ \t]*" | ||
| r"(?:\[[^\][{}\r\n]*['\"]\*['\"]" |
There was a problem hiding this comment.
This branch is not quote-aware: [^\][{}\r\n]* can consume the opening quote and scalar text, then treat an embedded quoted '*' as a top-level list element. For example, tools: ["grep --include='*'"] is falsely reported as unrestricted tool access. Please parse or narrow the list grammar so only an actual top-level "*" element matches, and add this scalar-string case as a negative regression.
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Re-reviewed current head f66907ab24d01c7acd9911d81eb9b9331959ff6f after the latest main synchronization. Both PR-owned blobs are byte-identical to reviewed head 3f6f63a3cd1d104dcb35fd1d7858d86461aaaacf; the current inline thread remains unresolved and has no reply. The blocker therefore remains: the same-line list regex is not quote-aware and still treats the embedded '*' in tools: ["grep --include='*'"] as a top-level unrestricted-tool grant. Make the matcher element/quote-aware and add that negative regression.
This head has no hosted check results and is not merge-ready.
Summary
Follow-up to #405 / #417 (and duplicate #438). #417 correctly bounded the EA1 wildcard-grant regex to a single line and a standalone
*, but an execution-verified edge-case audit of the merged pattern found two remaining false-positive paths and three detection gaps. This PR fixes all five.Fixes #444. Fixes #445. (Systemic paragraph-crossing across all 15 pattern files is tracked separately in #446 — out of scope here.)
Cases and how each is solved
False positives removed (#444)
Case 1 — footnote/legend text after a
Tools:label.The
(?!\*|\w)lookahead rejects**and*word, but a standalone*followed by a space passed — both lines fired EA1/MEDIUM on benign docs.Solution: a bare (unquoted)
*now only counts when it ends the line, optionally closed by]and/or a#comment. A real bare-scalar grant (tools: *,tools: [*],tools: * # allow all) has nothing else after the value; a footnote legend always does. Quoted forms ("*",'*') are unambiguous and keep matching anywhere on the line.Case 2 — blank-line gap before the colon.
#417 bounded the whitespace after the colon, but
\s*:before it still crossed newlines, so a markdown definition-list line two paragraphs later still bridged (matched texttools\n\n: *).Solution:
[ \t]*:— the key and colon must share a line. No real YAML/JSON/TOML syntax breaks a line between key and colon.Detection gaps closed (#445)
Case 3 — JSON quoted keys never matched.
The old prefix required the colon directly after the key word, so the closing quote of a JSON key broke the match — despite JSON being the most common encoding for MCP/agent configs.
Solution: the key may be wrapped in optional quotes:
['\"]?(?:tools?|permissions?)['\"]?.Case 4 — the idiomatic YAML block-list form never matched.
The block-sequence dash was never part of the pattern.
Solution: a second pattern matches a key followed by exactly one newline and a first list item that is a standalone wildcard. A blank line still breaks the match (the #405 cross-heading bridge cannot return), and the standalone-
*lookahead keeps markdown lists (- **Read**,- *note*) out. Later items are intentionally out of scope until seen in practice — matching arbitrary positions across lines widens the false-positive surface.Case 5 — wildcard not in first position of an inline list.
The old pattern required
*immediately after[.Solution: a quoted
*anywhere inside same-line brackets now matches (\[[^\]\r\n]*['\"]\*['\"]). Quoted-only on purpose: markdown link/bold text inside[...]cannot satisfy it, and an unquoted*mid-list in YAML is an alias, not a wildcard.Validation
analyze()flags (re.IGNORECASE | re.MULTILINE): 20 genuine grant forms still detected (quoted/bracketed/bare/no-space/tab/CRLF/uppercase/comments/allowed_toolssubstring), 11 new detections, 15 false-positive classes rejected (including all EA1 wildcard-tool-access pattern crosses blank lines and matches markdown bold syntax Affected rule: EA1 "Unrestricted Tool Access" Affected file: src/skillspector/nodes/analyzers/static_patterns_excessive_agency.py, line 45 #405/fix(ea1): bound wildcard-tool-access match to a single line and a sta… #417 regression cases).pytest tests/ -m "not integration and not provider"— 2,963 passed, 14 skipped, 4 xfailed, 0 failures.tests/nodes/analyzers/test_ea1_wildcard_line_boundary.py(footnote legend ×2, pre-colon gap, bare+comment, YAML block quoted/bare/zero-indent, JSON list/scalar key, inline not-first, markdown bold dash-list negative, blank-line-before-dash negative).ruff checkandruff format --checkclean.Risk
tools: [\n "*"\n]) and wildcards in later block-list items.