Rules / GRN-C009

diffwarnimplemented

GRN-C009 new-entry-point

Default severity warn for a group another tool loads by itself and for a new script with the name of a common command; info for any other new or changed entry point
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.2 (PyPI wheels)

What it checks

A wheel lists its entry points in *.dist-info/entry_points.txt: lines of name = target under a [group]. Garnet compares the new version's entry points with the trusted version's and reports an entry point (a group and a name) when the new version lists a target for it that the trusted version didn't, or when a line of it runs that didn't before.

Garnet reads them the way importlib.metadata does on Python 3.11 to 3.14, since pytest, setuptools and pip all find entry points through it:

It keeps its warn severity for:

Any other new or changed entry point, such as a new console script with the project's own name, is reported at info. The evidence lists the entry points that keep the warn first, 25 lines at most, and says how many more there are.

Why it matters

GRN-C003 reports code that runs when a package is imported. An entry point in an auto-loaded group runs code without anyone importing the package: installing a malicious pytest plugin is enough for it to run in every test session in that environment, with the project's secrets in the environment variables. A setuptools.finalize_distribution_options hook runs in every setuptools build there that doesn't isolate itself. Neither shows up as import-time code in the package itself.

Example finding

Illustrative; not from a real release.

examplelib 2.6.1 → 2.6.2: WARN
  WARN  GRN-C009 new-entry-point: 2.6.2 declares 1 entry point(s) that 2.6.1 didn't, including [pytest11] examplelib
           - [pytest11] examplelib = examplelib._plugin (new); [pytest11] is loaded automatically by the tool that reads it

How to resolve it

  1. Read the target the evidence names: the module, and the function after the colon.
  2. Find the commit that added or changed the entry point in the project's repository. A library that has never been a pytest plugin rarely becomes one in a patch release.
  3. If the repository explains the entry point, merge and add a time-limited ignore with the reason. If nothing there does, don't merge, and tell the maintainers.

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-C009"]
reason = "2.6.2 ships its pytest fixtures as a plugin, see the changelog"
until = "2027-06-30"

To change the rule's severity for every package instead:

[garnet.rules]
"GRN-C009" = "block"   # "off" | "info" | "warn" | "block"

Suppressed findings still appear in JSON and SARIF output, marked with the ignore that matched. How to write one is in the policy guide; the format is in SPEC §7.

What it doesn't check

See also