Rules / GRN-C002
GRN-C002 new-build-hook
| Default severity | warn |
| Kind | diff rule: fires only when the trusted version had the property and the new version lost it, or the new version gained a risky property the trusted version lacked. |
| Status | Implemented in 0.1 |
What it checks
Any of these, compared with the trusted version:
- a new or changed
setup.pyadds a call in the sensitive call set; - the build backend changed to one that is not on the allowlist, or to an in-tree backend;
- an npm
preinstall,install,postinstallorpreparescript was added or changed (npm support is planned for 1.0).
Build backends on the allowlist: setuptools.build_meta, setuptools.build_meta:__legacy__,
hatchling.build, flit_core.buildapi, poetry.core.masonry.api, poetry_core.masonry.api,
pdm.backend, pdm.pep517.api, maturin, scikit_build_core.build, mesonpy, uv_build,
pbr.build, whey, sipbuild.api, enscons.api.
Why it matters
Code in setup.py, a custom build backend or an npm install script runs on your machine when
the package is built or installed, before you ever import it. It runs in CI with whatever
credentials CI has.
Example finding
Illustrative; not from a real release.
examplelib 2.6.1 → 2.6.2: WARN
WARN GRN-C002 new-build-hook: 2.6.2 adds code that runs when the sdist is built
- setup.py:41 calls urllib.request.urlopen
How to resolve it
- Read the
setup.pydiff or the new backend named in the evidence. - Prefer installing a wheel if the release has one for your platform: wheels don't run
build code. (
pip install --only-binary :all:oruv pip install --no-build.) - If the build change is expected, merge and add an ignore for this version.
Allowlisting
Once you've reviewed a release, ignore this rule for that package and version range.
Every ignore needs a reason and an expiry date; after until it stops applying and the
report notes that it expired.
[[garnet.ignore]]
package = "examplelib"
versions = "==2.6.2"
rules = ["GRN-C002"]
reason = "setup.py now downloads a pinned, hash-checked data file; see example/examplelib#510"
until = "2027-06-30"
To change the rule's severity for every package instead:
[garnet.rules]
"GRN-C002" = "block" # "off" | "info" | "warn" | "block"
Suppressed findings still appear in JSON and SARIF output, marked with the ignore that
matched. The policy format is in SPEC §7; 0.1's garnet diff
doesn't read policy files yet.
Sensitive call set
A call is sensitive if it resolves, through imports and simple aliasing and without running any code, to one of these (SPEC §6.3):
exec,eval,compile,__import__importlib.import_modulewith a non-literal argumentos.system,os.popen,os.exec*,os.spawn*,pty.spawnsubprocess.*ctypes.*socket.*,urllib.request.*,http.client.*,httpx.*,requests.*,aiohttp.*base64.*decodefollowed by any of the abovemarshal.loads,pickle.loads
The set is versioned with the rule. garnet explain GRN-C002 prints the set your version uses.