docs(contributor-scan): 5.13 -- a taint request costs 6.6s, and the joins are not why

Four configurations over one verified graph (637 files, 7,061 methods, census
confirmed BEFORE anything was timed) and one 250-request set:

    both joins off   1657.5s   6,630 ms/request   5779 rows
    field half only  1623.0s   6,492 ms/request   5779 rows
    hook half only   1781.0s   7,124 ms/request   5779 rows
    both on          2026.3s   8,105 ms/request   5779 rows

The base query is the cost. The field half is free -- inside noise, measured
slightly faster -- the hook half adds 7.5%, both add 22%. Cutting them buys a
fifth of the time and costs two of the three CVEs.

Corrects a number I published earlier: ">0.94s per request" assumed taint
completed within its 2,400s ceiling. It timed out, so that was a floor dressed
as an estimate. The real figure is seven times higher.

2,500 requests is ~4.6 hours; all 4,463 regions would be ~41. Repository-scale
PHP is not slow, it is the wrong shape of question asked thousands of times.

Caveat kept with the numbers: all four configurations return an identical 5,779
rows because these are the TOP 50 regions -- REST handlers where framework
sources already exist and the joins add nothing. The joins earn their keep
lower down (`_delete_files` at rank 0.30). This measures their cost faithfully
and says nothing about their value; the two must not be conflated when deciding
what to cut.

Levers, none of them the arbiter: fewer and better regions (5.12, ~10x on its
own), fewer families per region (five are asked of every region regardless of
whether its scope holds a sink of that family), and callDepth -- three levels
over 7,061 methods is what makes a request cost seconds.

Also notes that commit 7308570 carried the OPENULTRASAST_CPG_QUERY_TIMEOUT
change under a docs message; `git add -A` swept it in and the message does not
say so.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-10 15:13:33 +02:00
co-authored by Claude Opus 5
parent 7308570ba6
commit a895bceefc
+37
View File
@@ -805,6 +805,43 @@
- _Requirements: 4.4, 6.1, 8.1, 9.1_
- _Depends: 5.8_
- [x] 5.13 What a taint request actually costs — measured, and it is not the joins
- The whole-repository scan fails on taint alone: `dominance` and `config` answer 13 requests in ~75s each
while `taint` exceeds a 2,400s ceiling on 2,500. Four configurations over ONE verified graph (637 files,
7,061 methods, census confirmed before timing anything) and ONE request set of 250:
| | seconds | per request | rows |
|---|---|---|---|
| both joins off | 1657.5 | **6,630 ms** | 5779 |
| field half only | 1623.0 | 6,492 ms | 5779 |
| hook half only | 1781.0 | 7,124 ms | 5779 |
| both on | 2026.3 | 8,105 ms | 5779 |
- **The base query is the cost, not the joins.** 6.63s per request with both halves disabled; the field
half is free (inside noise, and measured slightly faster), the hook half adds 7.5%, both add 22%.
Removing them buys a fifth of the time and costs two of the three CVEs, which is not a trade worth
making.
- **The estimate this replaces was wrong and worth recording as wrong.** ">0.94s per request" came from
assuming taint COMPLETED within its 2,400s ceiling. It timed out, so that was a floor presented as a
figure. The measured cost is seven times higher.
- Extrapolated: 2,500 requests (the current 500-region budget) is **~4.6 hours**, and all 4,463 regions
would be about **41 hours**. Repository-scale PHP is not slow, it is the wrong shape of question asked
thousands of times.
- **Caveat on the rows.** All four configurations return 5,779 rows -- identical. These are the TOP 50
regions, which are exactly the REST handlers where framework sources already exist and the joins add
nothing. The joins earn their keep lower down: `_delete_files` at rank 0.30 and `record` in WP
Statistics. So this measures their COST faithfully and says nothing about their VALUE, and the two must
not be conflated when deciding what to cut.
- The levers, in order of leverage, and none of them is the arbiter:
- **Fewer, better regions.** 50 well-chosen beats 500 mediocre, which is 5.12 and worth ~10x on its own.
- **Fewer families per region.** Five taint families are asked of every region regardless of whether the
scope contains a sink of that family at all. At `callDepth=0` that is exactly checkable and the empty
ones are free to skip; above zero it needs the reachable set.
- **`callDepth`.** Three levels of fan-out over 7,061 methods is the term that makes each request cost
seconds rather than milliseconds.
- _Requirements: 4.2, 4.3, 9.1_
- _Depends: 5.10_
- [ ] 5.12 The ranker knows one framework, and everything else lands in "unknown"
- > "the ranker hardcodes wp nodes etc. which means it's not generalizeable. I'd like the LLM to be a
> flexible layer here to avoid hardcoded / overly specific ranker implementations"