Security
Refactron's inputs are hostile by design. The diff under verification is written by an AI agent, a contributor or a codemod, and the test suite it runs belongs to the repository being verified. Both are treated as untrusted. This page states what that buys you, and what it does not.
Reporting a vulnerability
You will get an acknowledgement within 72 hours. For high or critical issues we coordinate disclosure with you and aim to ship a patched release before any public details are published. Please allow a reasonable embargo window.
Include a description, reproduction steps, the affected version, and the impact you believe it has. A reproduction we can run is worth more than a careful description of one.
Supported versions
| Version | Status |
|---|---|
0.4.x | Supported. Security fixes ship here. |
0.3.x | End of life. Contains known false-SAFE defects fixed in 0.4.x. |
0.2.x and below | End of life. Contains GHSA-q3vj-5qq5-m84g. |
Refactron is pre-1.0 and ships behaviour changes in patch releases. Security fixes are not backported below the current minor. Upgrade instead.
What Refactron is
A verification layer. A diff goes in; SAFE, UNSAFE or UNPROVEN comes out, backed by your repository's own test suite run in an isolated shadow tree with changed-statement coverage fused in.
It ships two binaries from one npm package: refactron, the CLI, and refactron-mcp, a stdio MCP server. The PyPI distribution is a shim that locates and executes the npm binary; it contains no engine code of its own.
The refactoring product, analyze, run, document, rollback, preflight, init, the AST transforms and the atomic batch writer, was removed in 0.4.0. Refactron no longer writes code. Anything you find describing a write path to your files describes something that does not exist.
What we defend
- Your working tree. Refactron never writes to it. Changes are applied to a copy under the system temp directory, the gates run there, and the copy is removed. There is no code path from a verdict to your files.
- Shadow-tree containment. A change whose path resolves outside the shadow tree is refused. Containment is resolved with
realpath, not string comparison, and repository symlinks whose target escapes the repository are not mirrored into the tree. - Diff intake. A path taken from a diff's
---/+++headers is refused before it is read if it resolves outside the repository. - Your credentials. The test suite Refactron runs does not inherit them.
REFACTRON_TOKEN, CI tokens, cloud keys, and any variable whose name ends in_TOKEN,_SECRET,_API_KEY,_PASSWORDor_CREDENTIALSare removed from the environment handed to every spawn that executes the suite. - Verdict integrity. A false
SAFEis the only unforgivable defect in this product. Every degradation path, a missing sidecar, an unmeasurable coverage run, a test command we cannot parse, a flaky heal, resolves toUNPROVENand never toSAFE.
What we explicitly do not defend
Stated plainly, because a guarantee with an unstated hole is worse than no guarantee.
- We do not sandbox your test suite. Running
refactron verify-diffruns your repository's tests. That is no more and no less safe than runningnpm testorpyteston the same repository yourself. A test that writes to an absolute path, opens a socket, or spawns a process will do so. Isolation means the shadow tree is a genuine copy, not that the suite is confined to it. - We do not sandbox the Python sidecars. They parse source with the standard library and LibCST. They do not execute it.
- The MCP server applies no authentication. For a stdio transport the trust boundary is the process spawn: whoever starts the server can already run arbitrary commands as you. It makes no network calls and reaches no remote service. Do not expose it over a network transport without adding authentication first.
- A `SAFE` verdict is not a proof of correctness. It means your suite ran the changed code and stayed green. It inherits exactly what your tests check, no more.
- Narrowing detection is a strong check, not a guarantee. Refactron reads the test command, the environment and your pytest configuration, and knows the common flags of
pytest,unittest,vitestandjest. A command using a flag it does not recognise reportsunknown, which does not cap the verdict. A vitestincludeor a jesttestMatchis not seen; those are JavaScript and would have to be executed rather than parsed.
Supply chain
- npm releases are published from GitHub Actions with an OIDC trusted publisher and
--provenance. Verify withnpm audit signatures. - PyPI releases use a trusted publisher. That attestation covers the shim, not the engine: the shim executes whichever
refactronbinary is first on yourPATH. If you pin the PyPI package, pin the npm package to the same version. - Releases are gated on
npm audit --audit-level=high. - Remediation is lockfile-only where possible. A declared range is not widened to clear an advisory.
Runtimes
Node.js 18 or newer is required. Python 3.8 or newer is required for coverage-backed verdicts and for the syntax, imports and statement-mapping sidecars. Without Python, verdicts degrade to UNPROVEN rather than failing open.
Past advisories
| Advisory | Affected | Fixed | Summary |
|---|---|---|---|
| GHSA-q3vj-5qq5-m84g | >= 0.2.0, < 0.4.2 | 0.4.2 | The shadow tree hardlinked unchanged files, so a verified test suite could write through into the caller's repository while the verdict reported SAFE. |
We filed that one against ourselves. The 0.4.2 entry has the full account, including the two related fixes that shipped with it and the fact that neither the working-tree guarantee nor shadow-tree containment had a test before that release.
Known dependency advisories
None outstanding. npm audit --audit-level=high reports zero vulnerabilities as of 0.4.3.