Composition¶
Where these rules come from and what a finding means: Rule Nature.
Distinct kinds of finding co-occurred. The combination is the signal.
H027 and H043 carry weight 0 by design: the points are already on the board from the rules that fired, and adding a score for the combination would double-count what those rules already scored. H098 is the deliberate exception. A package that both impersonates a popular name and redirects its dependencies or source is the attack H029 exists to surface, so the combination carries its own weight rather than relying on the sum of its parts. Its members still score individually; the cluster stacks on top by design, the same safety-first choice the X007 crossfire cluster makes.
H027 counts distinct capability category values across one diff. H043
counts distinct kill-chain stages. H098 counts distinct naming/deception
signals. None is a detection on its own: they annotate findings that other
rules produced.
See the rule system reference for the field table, the severity weights and the reserved identifier ranges.
Rules on this page¶
| Rule | Name | Severity |
|---|---|---|
| C021 | Dependency Removal During A Build Change | HIGH |
| C024 | Install Script Not Declared | MEDIUM |
| H027 | Capability Density Anomaly | INFO |
| H043 | Attack-Chain Composition | INFO |
| H098 | Naming/Deception Cluster | HIGH |
| H102 | Maintainer Change With Signing Key Change | HIGH |
H027: Capability Density Anomaly¶
- Target: programmatic (existing
triggered_rules, no new detection) - Severity: INFO (weight 0) - report-only co-occurrence flag
-
Category:
meta -
Condition: A single diff has rule hits in 3+ distinct capability categories (e.g.
network+filesystem+execution+encoding).
Most updates change one thing. A diff that simultaneously adds a network fetch, writes a file, and base64-decodes a payload is disproportionate; the co-occurrence is more suspicious than the sum of its parts.
Why weight 0: Adding a score for the combination would double-count; the three categories already scored individually via their own rules. Stacking extra points on top would inflate the benign p95, exactly the inflation the accuracy work eliminated. H027 therefore carries weight 0: it is a co-occurrence annotation surfaced to the report. The pattern is the signal; the points are already there.
Origin: Socket.dev's capability profiling - every package is annotated with a capability profile (network access, filesystem access, shell execution, encoded payloads) and Socket's diff view flags permission creep when a new version acquires capabilities it did not have before. H027 is the same insight at the rule-category level: a diff whose rule hits span multiple capability domains has a density that is itself a pattern.
H043: Attack-Chain Composition¶
- Severity: INFO (weight 0)
-
Category:
meta -
Condition: The findings on one package span at least
[thresholds] h043.attack_chain_stages(default 3) distinct kill-chain stages.
Stages: takeover (H026, H044, H074), mass adoption (H045, H073), install hook
(H023, H017), foreign fetch (R001, H035, H066, H034), payload (H068, H069),
obfuscation (H036, H065), anti-analysis (H067), write-then-execute (H072),
staging (H038), recon (H040), persistence (H039, H062, H076), exfil (H041,
H071), hidden drop (H042), integrity removed (H091) and sabotage (S001-S008).
Later rules also map onto these stages, including R041, R054, R144, the
H080-H083/H089-H091 additions and the X001-X034 crossfire rules; the
authoritative rule-to-stage map is _STAGE_OF in analysis/composition.py.
Each stage counts once however many rules in it fired, and H043's own finding is excluded from its own count. It is a composition annotation, not an additive score: the point is that several independent stages co-occurred, which is what separated the 2018 acroread attack and the 2026 Atomic Arch campaign from single-signal noise.
Fire rate: 0 of 3739. A benign diff with one or two rule hits cannot reach three distinct stages.
H098: Naming/Deception Cluster¶
- Target: programmatic (the package's own findings, no new detection)
- Severity: HIGH (weight 25)
-
Category:
meta -
Condition: The findings on one package carry at least
[thresholds] h098.min_signals(default 2) distinct naming/deception signals.
Members, each one independent:
| Rule | Signal |
|---|---|
| H029 | package name resembles a far more popular package |
| D001 | novel dependency added |
| D002 | typosquatted dependency |
| D004 | dependency hijack via provides/replaces |
| H064 | provides/replaces scope expansion |
| C012 | source domain resembles the declared upstream |
| H053 | name/host divergence |
| H059 | name/repo divergence |
| H026 | untrusted maintainer takeover |
One signal can be a coincidence. A package that is both named like a popular project and pulls in a novel or typosquatted dependency, or fetches from a domain resembling its declared upstream, is a method rather than a coincidence. H098 is a cluster annotation over findings other rules produced, and it runs after H029 so it can count the name finding. It is never fed back into H027 or H043, which keep their own counts.
Why weight-bearing, unlike H027 and H043: those annotate a single diff whose parts already scored separately, where a combination score would double-count. H098 is the deliberate exception for the naming/deception surface: the members are individually weak (a name, a dependency, a host) and the combination is materially stronger than the sum, so the cluster is scored once on top. The members still score; the stack is the point. Cite X007 for the same design in the crossfire family.
Fire rate: 0 of 3739. A benign diff with one or two rule hits cannot carry two naming/deception signals.
H102: Maintainer Change With Signing Key Change¶
- Severity: HIGH (weight 25)
-
Category:
meta -
Condition: The maintainer changed and
validpgpkeysmoved (H078) in the same diff.
The xz shape: a new maintainer plus a keyring change means the person now able to sign the sources is not the person the keyring was built for. Each fact scores on its own (H078, the maintainer tier); the conjunction is the attack, so it stacks the way X007 stacks its members. H078's own detection stands in for the keyring delta, the same member-rule composition H098 uses.
C021: Dependency Removal During A Build Change¶
- Severity: HIGH (weight 25) when
checkdependsloses an entry; MEDIUM (weight 15) whenoptdependsdoes. -
Category:
composition -
Condition:
checkdependsoroptdependsloses an entry whilebuild(),prepare()orcheck()changed in the same diff.
Dropping checkdepends eliminates the test-suite execution that would
catch a payload; the conjunction with a build change is what makes the
removal suspicious rather than hygienic. optdepends removal is far more
often ordinary cleanup, so it stays MEDIUM. Measured on the locked benign
corpus: 27/3,739 (0.72%) combined, the published calibration floor; the
fire-rate gate clears checkdepends for HIGH.
C024: Install Script Not Declared¶
- Severity: MEDIUM (weight 15)
-
Category:
composition -
Condition: A diff adds or modifies a
*.installfile while the recipe has noinstall=scalar naming it. pacman never runs the hook, so the file is recipe drift or a reverted declaration.
The dangling-declaration half (an install= naming an absent file) needs
tree knowledge and is not yet filed. Measured on the locked benign corpus:
12/3,739 (0.32%).