Skip to content

Commit 1940004

Browse files
authored
Merge branch 'main' into fix/pe3-env-lookbehind-and-reference-dir
2 parents 0a93f6f + 8e20d0e commit 1940004

2 files changed

Lines changed: 117 additions & 1 deletion

File tree

‎src/skillspector/nodes/analyzers/static_patterns_excessive_agency.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
# EA1: Unrestricted Tool Access
4444
EA1_PATTERNS = [
45-
(r"(?:tools?|permissions?)\s*:\s*\[?\s*['\"]?\*['\"]?\s*\]?", 0.85),
45+
(r"(?:tools?|permissions?)\s*:[ \t]*\[?[ \t]*['\"]?\*(?!\*|\w)['\"]?[ \t]*\]?", 0.85),
4646
(r"(?:allow|grant|enable)\s+(?:access\s+to\s+)?(?:all|any|every)\s+tools?", 0.8),
4747
(
4848
r"(?:no|without)\s+(?:tool|permission|access|capability)\s+(?:restrictions?|constraints?|limitations?)",
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""Tests for EA1's wildcard-tool-access pattern: line-boundary and bold-markdown fixes.
17+
18+
Confirmed on the official anthropics/skills repo, mcp-builder/SKILL.md:
19+
the original EA1 regex used bare ``\\s*`` between the colon and the expected
20+
wildcard value. Because Python's ``\\s`` matches newlines, the gap could span
21+
a blank line and bridge two unrelated headings (SKILL.md:96, "For each
22+
tool:" + blank line + "**Input Schema:**"). The pattern also had no check
23+
that the matched ``*`` was a standalone token, so the first ``*`` of a
24+
closing ``**`` bold-markdown span satisfied it (SKILL.md:25, "**API Coverage
25+
vs. Workflow Tools:**"). Both false positives fired as EA1/MEDIUM and
26+
survived to the final report.
27+
"""
28+
29+
from __future__ import annotations
30+
31+
from skillspector.nodes.analyzers import (
32+
static_patterns_excessive_agency as ea_module,
33+
)
34+
35+
36+
class TestEA1LineBoundary:
37+
"""The wildcard value must be on the same line as the colon, not bridged
38+
across a blank line to an unrelated heading."""
39+
40+
def test_heading_intro_followed_by_blank_line_and_next_heading_not_flagged(self) -> None:
41+
findings = ea_module.analyze(
42+
"For each tool:\n\n**Input Schema:**\n",
43+
"SKILL.md",
44+
"markdown",
45+
)
46+
assert not any(f.rule_id == "EA1" for f in findings)
47+
48+
def test_wildcard_on_next_line_without_blank_gap_still_not_flagged(self) -> None:
49+
"""The value must be on the *same* line as the colon; even a single
50+
newline before it is not a same-line wildcard grant."""
51+
findings = ea_module.analyze(
52+
"tools:\n*\n",
53+
"SKILL.md",
54+
"markdown",
55+
)
56+
assert not any(f.rule_id == "EA1" for f in findings)
57+
58+
59+
class TestEA1BoldMarkdownCollision:
60+
"""The matched ``*`` must be a standalone token, not the first asterisk
61+
of a ``**bold**`` span."""
62+
63+
def test_bold_heading_ending_in_tools_colon_not_flagged(self) -> None:
64+
findings = ea_module.analyze(
65+
"**API Coverage vs. Workflow Tools:**\n",
66+
"SKILL.md",
67+
"markdown",
68+
)
69+
assert not any(f.rule_id == "EA1" for f in findings)
70+
71+
def test_bolded_list_of_named_tools_not_flagged(self) -> None:
72+
"""A bolded list of specific tools is the opposite of an unrestricted
73+
grant and must not match."""
74+
findings = ea_module.analyze(
75+
"Tools: **Read**, **Write**\n",
76+
"SKILL.md",
77+
"markdown",
78+
)
79+
assert not any(f.rule_id == "EA1" for f in findings)
80+
81+
82+
class TestEA1GenuineWildcardStillFlagged:
83+
"""Real single-line wildcard grants must still fire — the fix narrows
84+
the pattern, it must not blind it."""
85+
86+
def test_tools_quoted_wildcard_flagged(self) -> None:
87+
findings = ea_module.analyze(
88+
'tools: "*"\n',
89+
"SKILL.md",
90+
"markdown",
91+
)
92+
assert any(f.rule_id == "EA1" for f in findings)
93+
94+
def test_tools_bracketed_wildcard_flagged(self) -> None:
95+
findings = ea_module.analyze(
96+
"tools: [*]\n",
97+
"SKILL.md",
98+
"markdown",
99+
)
100+
assert any(f.rule_id == "EA1" for f in findings)
101+
102+
def test_permissions_single_quoted_wildcard_flagged(self) -> None:
103+
findings = ea_module.analyze(
104+
"permissions: '*'\n",
105+
"SKILL.md",
106+
"markdown",
107+
)
108+
assert any(f.rule_id == "EA1" for f in findings)
109+
110+
def test_unquoted_wildcard_flagged(self) -> None:
111+
findings = ea_module.analyze(
112+
"tools: *\n",
113+
"SKILL.md",
114+
"markdown",
115+
)
116+
assert any(f.rule_id == "EA1" for f in findings)

0 commit comments

Comments
 (0)