Repository navigation
Found by pre-launch security reviews of 0.61.1. Ruleset 66.
Security
- The Claude Code hook no longer runs code from the project the agent works in. It started its scan with
python -m skilltotalfrom the agent's working directory, andpython -mputs that directory first onsys.path: ajson.pyplanted in an opened repository ran inside the scanner before anyone approved anything, and the project's.skilltotal.tomltuned the scan of someone else's package. The scan now starts in an empty directory of its own, with-Pon Python 3.11+. The npm runner's Python fallback passes-Ptoo, so it now needs Python 3.11+. - A package can no longer silence its own findings. A
skilltotal:ignorecomment suppressed a finding in any scanned component, so one line next to a dropper made it scan clean. Inline markers now count only forskilltotal scanof a local directory (your own code, as in CI).analyzeandanalyze_directorytakeinline_ignores, off by default, and an honored marker is counted inmetadata.suppressed_count. - The hook asks about every package it did not check. A scan that failed or ran out of time, the sixth and later packages of one command, and an install script that could not be read used to let the install through with a note, which made the guard easy to step around: pad a package until its scan times out, or name it sixth. They now need the person's approval. The hook still never denies an install because of its own failure.
- The hook checks what will actually be installed. The version a command names (
npx x@0.1.2,pip install x==1.0) instead of the latest release; the git ref (github:o/r#v1,git+https://github.com/o/r.git@main) instead of the default branch; the real package behind an npm alias (x@npm:real); andpip install -e git+.... Other git hosts (gitlab:,bitbucket:,gist:) and refs the scan cannot reproduce exactly are asked about. - The hook's verdict cache is keyed by the artifact. An unpinned
npm:xis first resolved to the release the registry serves (npm:x@1.2.3), and a PyPI package to the file pip would install, because a published PyPI version can gain files. A clean verdict from the morning no longer vouches for a release published in the afternoon. An exact release is reused for a week, a git repository or an unresolved name for an hour. - The GitHub Action passes its inputs to the shell only through
env(asourcecopied from a pull request comment could run commands in the runner), keeps the checked-out repository offsys.path, fails on an unknownfail-onvalue instead of silently disabling the gate, and runs the engine that ships with the pinned tag unlessversion:is set. - Text reports escape control characters, so a path or snippet from a scanned package cannot move the terminal cursor, rewrite earlier lines or set the clipboard.
Changed
- The scripts npm runs at install are always analyzed, whatever their shape. A minified one-line
setup.jsrun bypostinstallwas set aside as a bundle, so a dropper in it scanned clean. An install script that cannot be read at all (larger than 2 MiB, binary) is reported, makesguardblock and makes the hook ask. - PyPI packages are scanned as pip installs them: the pure-Python wheel when it is the release's only wheel, else the sdist, else any wheel, named by the registry. The sdist carried tests, docs and build tooling that pip never installs, which slowed the hook (fastapi: 1,215 files, now 65) and produced findings in code no user runs. A wheel's
*.dist-info/is packaging metadata, like an sdist'sPKG-INFO. - Source maps that carry no code are skipped. A
.mapholding only positions and names, as TypeScript writes it, cannot run and has nothing to analyze. A map withsourcesContent, or anything else in it, is read like any file. - npm dist-tags resolve.
npm:x@latestandnpm:x@next, the usual way MCP servers are started, scan the tagged version instead of failing. - The text report leads with the verdict, labels a capability finding as not scored (
[HIGH - capability, not scored]), shows a one-line location asfile:12, and lists three locations per finding (--jsonkeeps all of them). Verdict reasons list malicious indicators first, then scored risks, then capabilities. - The hook's messages name the release that was checked, the top finding of a high-risk package and the command to inspect it (
skilltotal scan npm:xinstead of<package>). - MITRE ATLAS tactics in the trait crosswalk are real ATLAS tactics. "Adversarial Perception Attacks" and "Model Poisoning" were labels from the CSA paper. An untrusted-instruction surface now maps to Execution and Persistence (AML.T0051, AML.T0070, AML.T0099), supply-chain provenance to Initial Access (AML.T0010).
- A usage error (an unknown flag value) exits 1, as documented; 2 means a gate tripped.
guardnames the malicious finding and its location instead of repeating the headline, andskilltotal scan lodashsuggestsnpm:lodashorpypi:lodash.
Fixed
- A redirected report no longer crashes on Windows. Output goes out as UTF-8 and
--outputis written before anything is printed, soscan --json > r.jsonon a legacy code page neither fails on a snippet nor leaves the file empty. - SkillTotal no longer rates its own repository as malicious.
RULES_CHANGELOG.mdquotes the attacks its rules detect, and the word "rules" made it read as an instruction file; a file whose name ends in a record word (changelog, history, license) is documentation. - False positives on widely used projects (ruleset 66): an injection phrase quoted by an issue-triage skill, a threat model's account of what attackers may do, a CLI's sandbox profile that denies its tools the CLI's own login file, a
0.0.0.0that is not a bind address, JSDoc examples, and popular packages whose names sit a letter away from another popular name no longer score. Each fix is pinned beside a test that the attack it resembles still fires. SeeRULES_CHANGELOG.md.