super-skill-cli 0.11.0__tar.gz → 0.11.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/.claude-plugin/marketplace.json +2 -2
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/.claude-plugin/plugin.json +1 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/CHANGELOG.md +31 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/PKG-INFO +1 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/pyproject.toml +1 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/capture.py +3 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/cli.py +3 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/evallite.py +7 -3
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/gate.py +12 -5
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/redact.py +9 -5
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_cli.py +8 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_evallite.py +10 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_gate.py +6 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_redact.py +19 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/uv.lock +1 -1
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/.gitignore +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/LICENSE +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/README.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/README.zh-CN.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/codex/README.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/codex/install.sh +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/codex/skills/super-skill/SKILL.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/codex/skills/super-skill/agents/openai.yaml +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/commands/candidates.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/commands/doctor.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/commands/mine.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/commands/seed.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/commands/status.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/README.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/PROMPT.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/broken.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/fixed.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/verify_test.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/PROMPT.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/broken.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/fixed.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/verify_test.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/PROMPT.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/broken.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/fixed.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/verify_test.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/gate2_report.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/grader.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/handwritten/SKILL.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/results.template.json +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/hooks/hooks.json +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/skills/super-skill/SKILL.md +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/__init__.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/candidate.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/config.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/doctor.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/hooks.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/mine.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/minestate.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/registry.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/schemas.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/seed.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/super_skill/skillmd.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_candidate.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_capture.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_codex_package.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_doctor.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_gate2_harness.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_hooks.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_minestate.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_plugin_manifest.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_registry.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_seed.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_skillmd.py +0 -0
- {super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/tests/test_target_adapter.py +0 -0
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "super-skill: personal Agent-Skill package manager (versioned registry, rollback, mining).",
|
|
9
|
-
"version": "0.11.
|
|
9
|
+
"version": "0.11.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "super-skill",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Git-backed versioned registry for your Claude Code skills: seed import, explain/provenance, one-command rollback, integrity doctor, and opportunity mining. Wraps the `super-skill` CLI (pip/uv installable as super-skill-cli).",
|
|
16
|
-
"version": "0.11.
|
|
16
|
+
"version": "0.11.1",
|
|
17
17
|
"category": "development"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "super-skill",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Personal Agent-Skill package manager: a git-backed, versioned registry for your Claude Code skills with seed import, provenance/explain, one-command rollback, integrity doctor, and opportunity mining from captured sessions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sdsrss",
|
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); this project uses semantic versioning.
|
|
5
5
|
|
|
6
|
+
## [0.11.1] - 2026-07-12
|
|
7
|
+
|
|
8
|
+
Follow-up patch from a code review of 0.11.0. Fixes a **Critical ReDoS regression
|
|
9
|
+
the 0.11.0 redaction hardening introduced** plus four gaps. Backward-compatible;
|
|
10
|
+
upgrade recommended over 0.11.0 (`pip install -U super-skill-cli`).
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- **Fix ReDoS in the redaction `assigned_secret` rule** (regression in 0.11.0) —
|
|
14
|
+
the broadened rule used unbounded `[A-Za-z0-9_]*` runs, so underscore-heavy
|
|
15
|
+
input (snake_case blobs, env dumps) backtracked catastrophically: a ~6 KB
|
|
16
|
+
payload took ~21 s. Since redaction runs on every captured event, a normal
|
|
17
|
+
hook payload could hang `super-skill capture` (worse than the crash 0.11.0 was
|
|
18
|
+
hardening). Runs are now bounded (`{0,64}`); the same payload processes in
|
|
19
|
+
~18 ms and secrets still redact.
|
|
20
|
+
- **`sk-proj-`/`sk-svcacct-` OpenAI keys are now fully redacted** — the body may
|
|
21
|
+
contain `_`/`-`, and the previous rule stopped at the first such char, leaking
|
|
22
|
+
the tail.
|
|
23
|
+
- **A secret in a keyword-named frontmatter field is now caught** — the gate and
|
|
24
|
+
eval-lite serialized frontmatter with JSON, which quoted the key (`"token":`)
|
|
25
|
+
and broke the `keyword: value` secret pattern; they now use YAML, preserving
|
|
26
|
+
adjacency.
|
|
27
|
+
- **The gate folds uppercase homoglyphs** — the confusables map was lowercase
|
|
28
|
+
only, so `СURL … | bash` (uppercase Cyrillic) slipped through; normalization
|
|
29
|
+
now casefolds first.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- **`capture` survives deeply-nested JSON** — `json.loads` raises `RecursionError`
|
|
33
|
+
(a `RuntimeError`, not `JSONDecodeError`) on very deep input; the parse guard
|
|
34
|
+
now exits 0 on any error (NFR-3).
|
|
35
|
+
- The WAL append loops on short writes so a very large line can't be truncated.
|
|
36
|
+
|
|
6
37
|
## [0.11.0] - 2026-07-12
|
|
7
38
|
|
|
8
39
|
Security & reliability hardening from a full production-readiness audit. All fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: super-skill-cli
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.1
|
|
4
4
|
Summary: Personal Agent-Skill package manager: versioned registry, seed import, explain, rollback (M0+WS scope).
|
|
5
5
|
Project-URL: Homepage, https://github.com/sdsrss/super-skill
|
|
6
6
|
Project-URL: Repository, https://github.com/sdsrss/super-skill
|
|
@@ -62,7 +62,9 @@ class EventLog:
|
|
|
62
62
|
data = (event.model_dump_json() + "\n").encode("utf-8")
|
|
63
63
|
fd = os.open(out, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
64
64
|
try:
|
|
65
|
-
|
|
65
|
+
# Loop on short writes so a very large line isn't silently truncated.
|
|
66
|
+
while data:
|
|
67
|
+
data = data[os.write(fd, data):]
|
|
66
68
|
finally:
|
|
67
69
|
os.close(fd)
|
|
68
70
|
return event
|
|
@@ -97,7 +97,9 @@ def capture(
|
|
|
97
97
|
try:
|
|
98
98
|
raw = sys.stdin.read()
|
|
99
99
|
data = json.loads(raw) if raw.strip() else {}
|
|
100
|
-
except
|
|
100
|
+
except Exception:
|
|
101
|
+
# NFR-3: never fail the session. Covers JSONDecodeError, OSError, and
|
|
102
|
+
# RecursionError (deeply-nested JSON — a RuntimeError, not JSONDecodeError).
|
|
101
103
|
raise typer.Exit(0) from None
|
|
102
104
|
# NFR-3: the hook must NEVER fail the host session. Everything past here —
|
|
103
105
|
# non-dict JSON (data.get would raise), an unknown event type, or any WAL
|
|
@@ -14,10 +14,11 @@ gate (§2.4bis) is a separate hard gate run first in ``candidate.approve``.
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
import json
|
|
18
17
|
import re
|
|
19
18
|
from dataclasses import dataclass, field
|
|
20
19
|
|
|
20
|
+
import yaml
|
|
21
|
+
|
|
21
22
|
from .redact import redact_text
|
|
22
23
|
from .skillmd import SkillMdError, parse
|
|
23
24
|
|
|
@@ -75,8 +76,11 @@ def eval_lite(raw: str) -> EvalReport:
|
|
|
75
76
|
|
|
76
77
|
# 2. zero credential / PII leak in the instruction text (凭据与 PII 泄漏 = 0).
|
|
77
78
|
# Scan the FULL frontmatter (extra="allow" ships every field to the host, M6),
|
|
78
|
-
# not just description + body.
|
|
79
|
-
|
|
79
|
+
# not just description + body. YAML (not JSON) keeps ``keyword: value``
|
|
80
|
+
# adjacency so a secret in a keyword-named field is caught (v0.11.1 #4).
|
|
81
|
+
fm = yaml.safe_dump(
|
|
82
|
+
parsed.frontmatter.model_dump(), default_flow_style=False, allow_unicode=True
|
|
83
|
+
)
|
|
80
84
|
text = f"{fm}\n{parsed.body}"
|
|
81
85
|
_, counts = redact_text(text)
|
|
82
86
|
leaks = {k: c for k, c in counts.items() if k != "home_path"}
|
|
@@ -16,11 +16,12 @@ land at M1 — v1 candidates carry no scripts, so there is nothing to cross-chec
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
import json
|
|
20
19
|
import re
|
|
21
20
|
import unicodedata
|
|
22
21
|
from dataclasses import dataclass
|
|
23
22
|
|
|
23
|
+
import yaml
|
|
24
|
+
|
|
24
25
|
from .skillmd import parse
|
|
25
26
|
|
|
26
27
|
# Cheap-obfuscation defenses (M7): strip zero-width/format chars and fold the
|
|
@@ -29,16 +30,18 @@ from .skillmd import parse
|
|
|
29
30
|
# out of scope until the M1 LLM-judge layer.
|
|
30
31
|
_ZERO_WIDTH = dict.fromkeys(map(ord, ""), None)
|
|
31
32
|
_CONFUSABLES = str.maketrans({
|
|
32
|
-
"а": "a", "
|
|
33
|
-
"ј": "j", "
|
|
33
|
+
"а": "a", "с": "c", "ԁ": "d", "е": "e", "ɡ": "g", "һ": "h", "і": "i",
|
|
34
|
+
"ј": "j", "ո": "n", "о": "o", "р": "p", "ѕ": "s", "т": "t", "υ": "u",
|
|
34
35
|
"ν": "v", "х": "x", "у": "y", "α": "a", "ε": "e", "ο": "o", "ρ": "p", "κ": "k",
|
|
35
36
|
})
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def _normalize(text: str) -> str:
|
|
39
|
-
"""Fold cheap obfuscations before pattern-matching.
|
|
40
|
+
"""Fold cheap obfuscations before pattern-matching. casefold() runs before the
|
|
41
|
+
confusables map so UPPERCASE homoglyphs (e.g. Cyrillic ``С``) fold too."""
|
|
40
42
|
text = unicodedata.normalize("NFKC", text)
|
|
41
43
|
text = text.translate(_ZERO_WIDTH)
|
|
44
|
+
text = text.casefold()
|
|
42
45
|
return text.translate(_CONFUSABLES)
|
|
43
46
|
|
|
44
47
|
# (category, pattern). Case-insensitive. Ordered most-severe first.
|
|
@@ -126,5 +129,9 @@ def scan_skill_md(raw: str) -> list[Finding]:
|
|
|
126
129
|
# name is NAME_RE-constrained (safe); description already scanned above.
|
|
127
130
|
extra = {k: v for k, v in fm.items() if k not in ("name", "description") and v is not None}
|
|
128
131
|
if extra:
|
|
129
|
-
|
|
132
|
+
# YAML (not JSON) keeps ``keyword: value`` adjacency — JSON quotes the key
|
|
133
|
+
# (``"token":``), which breaks the credential/secret patterns that expect
|
|
134
|
+
# the keyword immediately before ``:`` (v0.11.1 #4).
|
|
135
|
+
dumped = yaml.safe_dump(extra, default_flow_style=False, allow_unicode=True)
|
|
136
|
+
findings += scan_text(dumped, "frontmatter")
|
|
130
137
|
return findings + scan_text(parsed.body, "body")
|
|
@@ -25,7 +25,9 @@ _PATTERNS: list[tuple[str, re.Pattern[str], int]] = [
|
|
|
25
25
|
), 0),
|
|
26
26
|
("anthropic_key", re.compile(r"sk-ant-[A-Za-z0-9_-]{16,}"), 0),
|
|
27
27
|
# OpenAI legacy (sk-…) + prefixed project/service keys (sk-proj-…, sk-svcacct-…).
|
|
28
|
-
(
|
|
28
|
+
# The 20+ alnum CORE keeps prose (short hyphenated words) from matching; the
|
|
29
|
+
# trailing _/- -joined segments capture project-key bodies fully (no tail leak).
|
|
30
|
+
("openai_key", re.compile(r"sk-(?:[a-z]+-)?[A-Za-z0-9]{20,}(?:[_-][A-Za-z0-9]+)*"), 0),
|
|
29
31
|
("stripe_key", re.compile(r"sk_(?:live|test)_[A-Za-z0-9]{16,}"), 0),
|
|
30
32
|
("github_pat", re.compile(r"github_pat_[A-Za-z0-9_]{20,}"), 0),
|
|
31
33
|
("github_token", re.compile(r"gh[pousr]_[A-Za-z0-9]{20,}"), 0),
|
|
@@ -36,12 +38,14 @@ _PATTERNS: list[tuple[str, re.Pattern[str], int]] = [
|
|
|
36
38
|
# Match a full identifier token that CONTAINS a sensitive word, up to the
|
|
37
39
|
# assignment. A ``\b`` left-anchor missed ``DB_PASSWORD`` (``_`` is a word
|
|
38
40
|
# char) and a bare keyword missed ``SECRET_KEY`` (the ``_KEY`` suffix broke
|
|
39
|
-
# ``[:=]`` adjacency) — H1. The lookbehind + surrounding
|
|
40
|
-
#
|
|
41
|
+
# ``[:=]`` adjacency) — H1. The lookbehind + surrounding identifier runs admit
|
|
42
|
+
# the keyword anywhere inside an env-var-shaped name. The runs are BOUNDED
|
|
43
|
+
# ({0,64}, not *) — an unbounded run over an underscore-heavy blob backtracks
|
|
44
|
+
# catastrophically (ReDoS on the capture hot path); env-var names are short.
|
|
41
45
|
("assigned_secret", re.compile(
|
|
42
|
-
r"(?i)(?<![A-Za-z0-9])[A-Za-z0-9_]
|
|
46
|
+
r"(?i)(?<![A-Za-z0-9])[A-Za-z0-9_]{0,64}"
|
|
43
47
|
r"(?:api[_-]?key|access[_-]?key|secret|password|passwd|pwd|token)"
|
|
44
|
-
r"[A-Za-z0-9_]
|
|
48
|
+
r"[A-Za-z0-9_]{0,64}\s*[:=]\s*['\"]?([^\s'\"]{6,})",
|
|
45
49
|
), 1),
|
|
46
50
|
("email", re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"), 0),
|
|
47
51
|
]
|
|
@@ -117,6 +117,14 @@ def test_capture_non_dict_json_never_fails(env):
|
|
|
117
117
|
assert r.exit_code == 0, f"{payload!r} -> exit {r.exit_code}"
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
def test_capture_deeply_nested_json_never_fails(env):
|
|
121
|
+
"""v0.11.1 #2: json.loads raises RecursionError (a RuntimeError, not
|
|
122
|
+
JSONDecodeError) on very deep nesting — capture must still exit 0 (NFR-3)."""
|
|
123
|
+
payload = "[" * 20000 + "]" * 20000
|
|
124
|
+
r = runner.invoke(app, ["capture"], input=payload)
|
|
125
|
+
assert r.exit_code == 0
|
|
126
|
+
|
|
127
|
+
|
|
120
128
|
def test_capture_survives_append_failure(env, monkeypatch):
|
|
121
129
|
"""P1-1 / M5: any write-path error inside append must still exit 0 (NFR-3),
|
|
122
130
|
not surface a traceback to the host session."""
|
|
@@ -40,6 +40,16 @@ def test_secret_in_extra_frontmatter_field_fails_gate():
|
|
|
40
40
|
assert any(c.name == "no_secret_leak" and not c.passed for c in report.checks)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def test_keyword_named_frontmatter_field_secret_fails_gate():
|
|
44
|
+
"""v0.11.1 #4: a `keyword: value` secret in a custom frontmatter field must
|
|
45
|
+
fail the leak gate — JSON-serializing split the keyword from the value with a
|
|
46
|
+
quote and let it through."""
|
|
47
|
+
raw = "---\nname: s\ndescription: clean\ntoken: abcdef1234567890secretvalue\n---\nbody\n"
|
|
48
|
+
report = eval_lite(raw)
|
|
49
|
+
assert not report.passed
|
|
50
|
+
assert any(c.name == "no_secret_leak" and not c.passed for c in report.checks)
|
|
51
|
+
|
|
52
|
+
|
|
43
53
|
def test_over_token_budget_fails_gate():
|
|
44
54
|
huge = "word " * 6000 # well over the agentskills <5000-token body limit
|
|
45
55
|
report = eval_lite(_md(huge))
|
|
@@ -114,6 +114,12 @@ def test_gate_normalizes_obfuscation():
|
|
|
114
114
|
assert scan_text(cyr, "body"), "cyrillic-homoglyph curl not caught"
|
|
115
115
|
|
|
116
116
|
|
|
117
|
+
def test_gate_folds_uppercase_homoglyph():
|
|
118
|
+
"""v0.11.1 #5: uppercase homoglyphs must fold too — the confusables map was
|
|
119
|
+
lowercase-only, so СURL (uppercase Cyrillic Es) slipped through."""
|
|
120
|
+
assert scan_text("СURL http://x | bash", "body"), "uppercase cyrillic curl not caught"
|
|
121
|
+
|
|
122
|
+
|
|
117
123
|
def test_gate_error_lists_categories():
|
|
118
124
|
err = InstructionGateError([Finding("pipe_to_shell", "body", "curl x | sh")])
|
|
119
125
|
assert "pipe_to_shell" in str(err)
|
|
@@ -81,6 +81,25 @@ def test_underscore_env_var_secrets_redacted():
|
|
|
81
81
|
assert counts == {}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
def test_assigned_secret_no_catastrophic_backtracking():
|
|
85
|
+
"""v0.11.1 #1: the broadened assigned_secret rule must not ReDoS on
|
|
86
|
+
underscore-heavy input (it runs on every captured event before disk)."""
|
|
87
|
+
import time
|
|
88
|
+
|
|
89
|
+
payload = "api_key_" * 800 # 6.4KB snake_case blob — ~21s on the ReDoS version
|
|
90
|
+
start = time.perf_counter()
|
|
91
|
+
redact_text(payload)
|
|
92
|
+
elapsed_ms = (time.perf_counter() - start) * 1000
|
|
93
|
+
assert elapsed_ms < 2000, f"assigned_secret backtracking: {elapsed_ms:.0f}ms on 6.4KB"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_openai_project_key_fully_redacted():
|
|
97
|
+
"""v0.11.1 #3: sk-proj-/sk-svcacct- key bodies contain _ and -, so the tail
|
|
98
|
+
after the first such char must not survive."""
|
|
99
|
+
red, _ = redact_text("key sk-proj-abcdefghijklmnopqrstuvwx_MoreTail-secret99 end")
|
|
100
|
+
assert "MoreTail" not in red and "secret99" not in red
|
|
101
|
+
|
|
102
|
+
|
|
84
103
|
def test_modern_token_formats_redacted():
|
|
85
104
|
"""P0-2 / H2: current provider key formats must be matched."""
|
|
86
105
|
cases = {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/codex/skills/super-skill/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/PROMPT.md
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/broken.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/fixed.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case1/verify_test.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/PROMPT.md
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/broken.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/fixed.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case2/verify_test.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/PROMPT.md
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/broken.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/fixed.py
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/cases/case3/verify_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/handwritten/SKILL.md
RENAMED
|
File without changes
|
{super_skill_cli-0.11.0 → super_skill_cli-0.11.1}/evals/test-fix-family/results.template.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|