SkillSpector 2.13.1
This patch release is based on the published v2.13.0 maintenance branch. It corrects false incomplete-analysis reports for ordinary Python report strings, quoted script text, regular expressions, and possessive Markdown prose, and false P2 hidden-instruction findings that spanned visible text between HTML comments. It also includes quoted-version metadata and passive documentation image handling, validated Office worksheet and printer metadata support, and faster security text normalization.
The maintenance backports include three security fixes: safer report and baseline file output, incomplete-coverage reporting for failed batch gap-fill passes, and bounded tool-misuse context and deadlines. Five are analyzer, report and CLI fixes: TM1 repeated assignments, MP3 hardware reset states and state adoption, recursive MEDIUM recommendations, and rejection of unsupported recursive and registry option combinations. A further customer fix prevents ordinary code comparisons from causing incomplete inspection.
The selected changes below include the minimal command-token prerequisite from #697 and the prohibition-context prerequisite from #658 used by MP3; the remaining changes from those PRs are excluded.
Selected changes
-
Office worksheets identified by matching content types and workbook relationships can use the existing 16 MiB analysis allowance. This lets the reported 1.97 MB worksheet receive complete static inspection. Unrelated archive members keep their 1 MB limit; shared expansion, compression, integrity and runtime limits remain enforced.
-
Validated Office printer settings no longer receive executable-content findings solely because their filenames end in
.bin. Content types, relationships and the complete printer-record structure must agree. Malformed records, executable signatures and independent findings retain their safeguards; opaque driver data keeps explicit inspection limits. -
Security text normalization now reuses bounded character caches and processes unchanged text in bounded chunks. This reduces PDF/GIF scan overhead while preserving findings, source offsets, cancellation checks and existing limits.
-
Shell heredoc parsing retains quote ownership for Python formatted strings, avoiding false unfinished-shell reports.
-
Follow-up parser corrections preserve opening quotes in inline JSON examples and inline-code delimiters in ordinary nested Markdown list paragraphs. Quotes cannot incorrectly consume later examples; actual shell bodies and uncertain block contexts retain their existing checks and bounds.
-
Explicitly documented dotted configuration fields no longer invent missing files when the bundle contains no matching artifact. Explicit file uses, existing files and ambiguous basename matches retain normal accounting.
-
#525 treats quoted version numbers in YAML version fields as metadata. Real file references, including numeric filenames, keep their normal missing or resolved status.
-
#877 avoids format-only AE1 findings for corroborated passive PNG/JPEG documentation images. Exact source provenance, supported image structure and complete limitation evidence are required. Image coverage remains partial; this does not perform OCR or image semantic inspection. Executable uses, malformed images and independent findings retain their safeguards.
-
#777 keeps comparisons such as
len(value.strip()) < 12from being read as an unfinished tag-removal instruction. Real tag directives, including ones after a comparison, retain their existing detection and incomplete-inspection safeguards. Its quoted-command controls also require thecurlactive-token entry from #697. This release includes that one-token prerequisite; the broader flag and mapping-key exemptions from #697 remain outside this patch. -
#778 establishes bounded Python and Perl host-language ownership for the shell parser.
-
#784 shares validated Python quote ownership with marker reconstruction.
-
#849 corrects prose and report-string parsing, using those prerequisites.
-
Additional release hardening retains the previous incomplete-inspection result when a report-shaped string is later assembled into a shell command. The check covers recognized local execution and a bounded set of bundled helpers, including import-time initialization. It does not lower findings or suppress an incomplete scan.
-
#897 corrects the release's bundled-helper proof so imported setup functions, callbacks, defaults and decorators cannot hide shell execution behind a false complete/SAFE result.
-
Deep expressions in bundled Python helpers retain conservative shell bounds when the optional import proof exceeds Python's serialization recursion limit, instead of causing an analyzer failure.
-
#452 keeps each P2 hidden-instruction match inside one HTML comment, including the
--!>,<!-->and<!--->closers. Keywords match at word and identifier boundaries, so visible text and words such astargetorPOSTGRESare not reported as hidden instructions. Run-together directives such asPLEASEIGNOREPREVIOUSINSTRUCTIONSremain detected. -
Source distributions exclude the
.uv-cachedirectory used by the release workflow.
The release preserves v2.13.0's third-party dependency lock and provider and model defaults. Publication supports the selected maintenance branch. It does not take the remaining unselected changes from main.
Backported security fixes
-
#790 makes the tool-misuse analyzer reuse one line index for finding context, so files with thousands of tool-misuse matches are no longer re-read in full for each new finding line. The TM1–TM4 checks and the Perl print helper now stop when the per-file static analysis time limit is reached, instead of only after the whole file is scanned.
-
#797 keeps core findings and findings from completed batches when the contributed batch scanner's non-English gap-fill pass fails part-way. The skill is reported as incomplete (
gap_fill_status: "incomplete"with failure reasons and at least a CAUTION recommendation), and the batch run exits with code 2. Previously the failed pass was treated as complete with no findings. -
#798 writes report files (
scan,scan --recursive,scan --mcp-registryand the contributed batch scanner's--output) and baselines atomically through an already-opened, no-follow parent directory. SkillSpector refuses symlinked or special-file destinations, symlinked parent directories and report files it cannot open for writing. Baselines also enforce their existing write-bit restrictions. Temporary report files have inherited access permissions, including macOS ACLs, cleared before any report content is written. This includes the correction tracked in #896.skillspector baseline <path> -o -writes the baseline as JSON to stdout.
Backported fixes
-
#820 makes TM1 use the final value of a repeated Python assignment target. A flag that was set true and then reassigned with
enabled, enabled = True, Falseno longer makes a latershell=enabledcall a HIGH finding. The earlier true value is dropped only when the final value is provably false, the right-hand side is passive, and earlier bindings are proven safe in a straight-line module or function scope. Other cases keep the existing conservative result. -
#823 rejects
--use-shipped-baselinewith exit code 2 whenscan --recursivedetects one or more child skills, before any report is written. Previously the option was silently ignored. A single-skill scan still accepts it with--recursive. -
#836 reports a complete recursive scan whose highest score is MEDIUM (21–50) as CAUTION, matching the child reports, instead of SAFE. Execution failures still report DO_NOT_INSTALL, incomplete analysis still raises SAFE to CAUTION, and exit codes are unchanged.
-
#837 stops MP3 from reporting descriptions of a clocked hardware or state-machine "reset state" as memory manipulation, while keeping direct agent-reset instructions. It adds a HIGH MP3 finding for an explicit instruction to treat or adopt (linked) state as the agent's memory or conversation state, unless that instruction is directly prohibited.
-
#840 rejects
scan --mcp-registrycombined with--use-shipped-baseline,--transitive,--transitive-depth,--transitive-allow-prefixor--transitive-deny-prefixwith exit code 2, before the registry is read or a report is written. Previously these options were silently ignored.