Skip to content
Research13 min readSprino team

What AI finds in code that pattern matching cannot

Three AI harnesses produced 1,318 findings across 4.8 million lines of production code. A third sat in authorization, IDOR and race classes that conventional SAST has no rule for. Almost none reproduced between runs, and none had been verified.

Summary and key findings

We ran three AI security harnesses, Opus 4.8, Mythos 5 and the Codex security suite, over four production codebases in a commercial multi-tenant product. The corpus is roughly 4.8 million lines, drawn from an estate of more than seventy repositories. The harnesses produced 1,318 findings across 202 distinct CWEs. We triaged the Opus and Mythos exports to reviewer verdicts: 87.3 percent precision for Opus, 91.6 percent for Mythos. Codex was not triaged.

Five results are stable enough to state up front.

  • 492 of 1,318 findings, 37.3 percent, sat in authorization, authentication, IDOR or race classes. On the multi-tenant web application that share was 62 percent. No pattern-matching tool in the estate had reported these classes
  • The three models specialized by language and by defect class. Codex overlapped the other two harnesses on 0 to 3 percent of files, and on zero files on the web application
  • Repeated runs over the same code agreed on 0 to about 2 percent of findings. They still agreed on the class distribution: authorization ranked first in every web-application run
  • At the point we pulled this data, all 1,318 findings were still open. Median reviewer time was 99 minutes per finding
  • Class-level coverage was a property of the codebase, not of a global scanner setting. Native code produced memory-safety and filesystem findings; the web application produced authorization and object-level access findings

The rest of this note is the evidence for those claims, then the limits of that evidence. Figures are observations. What we take them to mean is written after the figure they rest on.

4.8M
Lines of production code analyzed
1,318
Findings across three harnesses
202
Distinct CWEs represented
37.3%
In authorization, authentication, IDOR or race classes

What we measured and which populations we compared

The estate splits into one large managed-code application and three native codebases. That split is the comparison that organizes everything below: the same three harnesses, two language families, and several repeated runs per codebase between June and September 2026.

Codebases and findings by model
CodebaseLanguageLinesOpusMythosCodex (non-info.)
Multi-tenant web applicationKotlin, Java3,801,01954189101
Endpoint agentC, C++615,6165511987
Native serviceC, C++384,9855110755
Native componentC, C++—358434

The Codex column excludes 126 informational findings (277 of 403 shown); the following density chart includes all 403. Language is inferred from file paths: the web application is 62 percent Kotlin and 34 percent Java, and the three native codebases are 82 to 93 percent C and C++.

The 3.8 million line web application produced the largest absolute number of findings and by far the lowest density. Across all three models, the two measured native codebases were roughly six times denser per line.

Findings by codebase: raw count against density
Opus 4.8Mythos 5Codex
  • Web applicationKotlin, Java, 3.80M lines
    14
    50
    31
  • Endpoint agentC, C++, 0.62M lines
    89
    193
    279
  • Native serviceC, C++, 0.38M lines
    132
    278
    179
  • Native componentC, C++
    Density unavailable

This chart uses 1,097 attributed findings: Opus 195, Mythos 499 and all 403 Codex findings, including 126 informational. The native component remains visible in density mode, but no rate is shown because a reliable line count was unavailable.

We compared four populations, not one pooled total. Codebases, because managed and native code do not share a defect distribution. Models, because Opus, Mythos and Codex are different configurations, not three draws from one scanner. Runs, because the same harness over the same code is still a sample. Defect classes, because a finding count is not a coverage statement until it is pinned to a taxonomy.

Opus and Mythos already exported a CWE, so those 694 findings joined a shared twelve-class grouping without a second pass. Codex did not: that export is title, description, severity and paths. To put all three models on one chart we classified every Codex row with Jev 1.13.0, TypeSafe's typed decision model. Each call sent title, severity, language and a scrubbed description, never emails, repositories or file paths. Jev answered three questions in one pass: the twelve defect classes, the CWE catalog Opus and Mythos already used, and whether the chosen CWE was a clear match rather than a forced fit. Class confidence was high, median 0.96. The specific CWE was more spread, median 0.47. Most of the 126 informational Codex rows have no clean CWE, 35 of them untagged; they still sit on the charts so the three models are counted the same way.

What Jev isHow the API worksTypeSafe

A later run counted as the same finding when it named the same file and the same CWE. Precision figures are our own reviewer triage of Opus and Mythos on a single codebase. Codex was never triaged, so no precision figure is quoted for it. Language is inferred from file extensions. Those choices are the scope of the comparisons below; the limits they impose are in the Limitations section.

Where application context matters

Across all runs, 492 of 1,318 findings fall into authorization, authentication, object-level access and race classes: 297 authorization and RBAC, 110 authentication and session, 45 IDOR and 40 races. On the multi-tenant web application that share is 62 percent.

Most common CWEs across all runs
CWEDescriptionFindings
CWE-862Missing authorization120
CWE-639Authorization bypass through user-controlled key76
CWE-863Incorrect authorization63
CWE-306Missing authentication for critical function40
CWE-125Out-of-bounds read39
CWE-532Sensitive information written to log file34
CWE-367Time-of-check time-of-use race22
CWE-59Improper link resolution before file access26

The top three CWEs account for 259 findings between them. None of them has a dangerous function at the center. A taint rule works by recognizing untrusted data arriving at a sink. There is no sink here. The defect is that a check which should exist does not, or runs in the wrong order, or validates the wrong subject.

CWE-639 is the clearest case. An endpoint accepts an identifier, looks up the record and returns it, and every individual step is correct. The bug is that nothing ties that identifier to the caller's tenant. To see it you have to know that the application is multi-tenant, know which table carries the tenancy column, and know that this particular handler is reachable by an authenticated user of a different tenant. That is application knowledge, not a syntactic pattern. A linter rule for it would either never fire or fire on every database read in the codebase.

Role-based access control fails the same way. A role is checked, but after the data is fetched, or against a role that is necessary rather than sufficient, or on the wrong object in a delegated session. Race conditions are harder still: the 22 time-of-check time-of-use findings and the 40 race findings overall depend on two operations being separated in time under concurrency, which is a property of execution rather than of syntax. Path-sensitive analyzers can model some of these; in practice they accumulate false positives long before they reach the interesting ones.

The 1,318 findings carry 328 distinct category labels, and 180 of those labels appear exactly once. That long tail is where business logic lives: a missing state-transition validation, a fail-open authorization branch, an SSO state fixation, a path equivalence bypass, a capture-replay authentication bypass, an encoding confusion between two layers that each behave correctly alone. A rule for a defect that appears once, in one application, has no second customer. A model that reads the code can still surface it.

We went through the public Semgrep rule set. Pattern matching is built for secrets and injection. The three CWEs that dominate this estate barely appear, and none of those files is a Kotlin or Java tenant check. The pack also has no feedback loop that would turn a confirmed finding into more variants for the next AI pass.

Public Semgrep files against findings in this estate
2,193
YAML files in the pack
8
Files on CWE-862, 639, 863
0
Kotlin or Java tenant rules found in snapshot
Semgrep filesFindings here
  • CWE-798 Hardcoded credentials266 files · 14 findings
  • CWE-79 XSS130 files · 16 findings
  • CWE-89 SQL injection80 files · 3 findings
  • CWE-78 Command injection65 files · 28 findings
  • CWE-862 Missing authorization5 files · 120 findings
  • CWE-639 User-controlled key1 files · 76 findings
  • CWE-863 Incorrect authorization2 files · 63 findings
  • CWE-367 TOCTOU race1 files · 22 findings

Pack counts are unique YAML files in semgrep/semgrep-rules. Finding counts are vendor CWEs from the Opus and Mythos runs, the same 259 that sit on CWE-862, 639 and 863.

Application context also decides which of these classes exist at all. Sorting every finding by the language of its file makes that split measurable.

How findings flow from model to defect class to language
1097 attributed findings
ModelDefect classLanguageOpus 4.8195Mythos 5499Codex403Authorization, RBAC247Memory safety114Injection, input112Secrets, exposure98Authn, session77Integrity107Crypto, transport51Filesystem63Resource exhaustion56Races, TOCTOU32IDOR33Other107C/C++628Kotlin242Java98Scripts, config129

Every column sums to 1,097 findings: 694 from Opus and Mythos plus all 403 Codex findings whose CWEs Jev assigned, including 126 informational. Node heights and ribbon widths are directly comparable. Hover or focus any node to isolate its flows. Read it right to left and it also answers the reverse question: which models reach a given language at all.

Memory safety lands 113 findings in C and C++ and exactly zero in Kotlin or Java, which is the one result here that a runtime guarantees rather than a scanner. Filesystem is 48 native against one in Java. Object-level authorization stays a managed-code problem: 26 of 33 IDOR findings are in Kotlin or Java, because that is where the multi-tenant request handlers live. Authorization overall is the class that shows up everywhere. Codex is what thickens that band: 83 of its 403 findings, 46 of them in Kotlin on the web application.

What we take this to mean: the defect classes worth hunting are a property of the codebase, not a global setting. A native agent needs memory safety and filesystem race coverage. A multi-tenant application server needs authorization, tenant isolation and object-level access coverage, and gains nothing from a memory safety pass. One scanner configuration for both produces a queue that can be technically accurate and still aimed at the wrong classes.

Differences between configurations

Mythos found about 2.6 times the raw volume of Opus. Codex sits between them at 403 findings, 277 of them actionable. Volume alone would pick Mythos. Normalizing each model's findings against its own total shows a different picture: the three configurations are good at different things, and the gaps are large enough to measure.

How each model spends its findings
Opus 4.8Mythos 5Codex
  • Races, TOCTOU
  • Secrets, exposure
  • Filesystem
  • Authorization, RBAC
  • Resource exhaustion
  • Integrity
  • IDOR
  • Authn, session
  • Injection, input
  • Memory safety
  • Crypto, transport

Each bar is that model's share of its own output, so Mythos and Codex returning more overall does not stretch their bars. Opus 195, Mythos 499, Codex 403 after the Jev pass described above. The residual Other bucket is not shown: Opus 9, Mythos 48, Codex 50.

Opus devoted a fifth of its output to memory safety, against 9.6 percent for Mythos and 6.5 percent for Codex, and it leads on crypto and transport defects. It also grades hotter: 38 percent of its findings rated high, against 28 percent for Mythos and 5 percent for Codex. Mythos owns the concurrency and hygiene classes: it found races at 6.6 times the Opus rate, secrets and exposure at 3.9 times, and filesystem defects at 2.7 times. Codex spends 21 percent of its output on authorization and 17 percent on integrity, and almost nothing on injection. Jev put 16 of Codex's 20 highs on CWE-862, and every one of those 20 highs arrived with a proposed patch. File-level overlap with the other two harnesses was 0 to 3 percent, and zero on the web application.

How each model grades its own findings
  • Opus 4.8

    37.9% High

  • Mythos 5

    27.9% High

  • Codex

    5.0% High

High: Opus 4.8 74 of 195 (37.9%), Mythos 5 139 of 499 (27.9%), Codex 20 of 403 (5.0%)

Each pie is that model's share of its own output. Hover a slice for its percentage. Click a grade to compare it across models. Opus 74 of 195 high, Mythos 139 of 499, Codex 20 of 403. Codex looks colder partly because 126 of its rows are informational, a bucket the Opus and Mythos exports did not include. Of the 277 actionable Codex rows, 7 percent are high. Severity labels are source-authored and were not calibrated across configurations.

The same split shows up by language. Opus put 69 percent of its findings in C and C++ and 7 percent in Java. Mythos spread wider, at 53 percent native, 12 percent Java and 5 percent shell scripts. Codex is 57 percent native and 22 percent Kotlin, which is where its authorization budget went. The per-CWE view is consistent: Opus over-indexes on out-of-bounds reads and writes, server-side request forgery and path traversal; Mythos over-indexes on time-of-check races, link resolution, file permissions and credentials in logs; Codex over-indexes on missing authorization.

What we take this to mean: the useful question is not which model is best. It is which model is best at the class you decided to hunt, on the language you are hunting it in. One frontier model on one pass leaves whole categories uncovered. A report does not list what the model was bad at, so those gaps are not visible to the buyer of a single opaque pass.

What repeated runs reproduced and what changed

Matching by file and CWE, repeated runs over the same code almost never agreed on the individual finding.

Shared findings between every pair of runs
Shared findings between each pair of runs over the Web application
R1R2R3
R1
R2
R3
Distinct findings across all 3 runs
487
Best single run
258

Select a cell to compare two runs. The diagonal is each run's own size.

The diagonal is each run's own size, so the story is the emptiness off it. Switch codebases and the pattern holds. On the native service, three runs executed against an identical commit and still agreed on 0, 0 and 1.1 percent of findings, so this is not code churn.

Five runs over the endpoint agent produced 382 distinct findings. The best single run found 116. On the native service, three runs against an identical commit agreed on 0, 0 and 1.1 percent of findings. Finding-level agreement elsewhere sat near 2 percent.

The instability is at the level of the individual finding, not the class. On the web application, authorization ranked first in every run at 43, 60 and 56 percent of findings. Roughly half of any run's findings fell inside another run's eight most common CWEs. Five pooled runs over the endpoint agent returned 3.3 times the distinct findings of the best single run.

The labels moved too. Those 1,318 findings carry 328 distinct free-text category labels. A single concept, missing or incorrect authorization, appeared under fifteen different labels, including missing authorization, improper authorization, incorrect authorization, authorization bypass, fail-open authorization, IDOR, IDOR/BOLA, broken object level authorization, and three separate spellings of that last one. Group by CWE and the same data is stable: 120 findings at CWE-862, 76 at CWE-639, 63 at CWE-863.

What we take this to mean: you cannot call a release clean on a single draw, because the next run returns a different set. You cannot tell whether last quarter's remediation reduced risk, because a drop in findings is indistinguishable from a different sample. You cannot gate a pipeline on a fresh draw, and you cannot dismiss a finding with confidence because the corroborating run never happened. The class distribution is stable enough to aim at, budget against and measure. The individual finding is not. If runs barely overlap they are not redundant, so pooling compounds rather than repeats, but only after every finding is normalized onto a fixed taxonomy. A dashboard built on model-authored labels is not a measurement.

Why verification became the bottleneck

At the point we pulled this data, all 1,318 findings were still open and none had been triaged to a verdict. The measured median reviewer time was 99 minutes per finding, which puts about 1.36 engineer-years between this report and any change to the product. In one export, 20 of 403 findings came with a proposed patch, about 5 percent. Whole-repository passes run for hours. Roughly twenty repositories in this estate could not be onboarded at all because the scan exceeded the available budget.

The remediation pipeline, as measured

The queue had not moved at the point we pulled the data

Click a stage. The empty one is the point: detection produced a queue and nothing in it had been worked.

Each finding costs 99 minutes because a model assertion is not a decision. The reviewer relocates the affected path, works out whether anything upstream already enforces the missing check, decides whether the path is reachable from a real entry point, judges the blast radius against the tenancy model, then designs a fix that does not break a caller. The report contributed a claim and a severity label. The reviewer rebuilds the analysis that justifies acting on it.

What we take this to mean: detection was never the constraint. The scarce work is deciding what deserves action and attaching evidence a reviewer does not have to recreate. Hours-long whole-repository passes also keep that work outside the pull request, so everything above is archaeology, found after the code shipped. Scoping to the changed lines and the code reachable from them is what moves the same machinery into a merge gate. It costs a few dollars for a 200 line diff, against well under one percent of a full-corpus pass for a 2,000 line one.

Limitations

These figures come from harness runs over one commercial multi-tenant estate, anonymized, between June and September 2026. They are not an industry benchmark, and we would expect the magnitudes to move on different architectures.

  • Model attribution for Opus and Mythos rests on matching per-run finding counts to the recorded totals for each harness
  • Codex is a separate export of 403 findings, 277 of them actionable, with no vendor CWE or category field. Those CWEs and the twelve defect classes were assigned by Jev 1.13.0 so Codex could be compared on the same taxonomy
  • The Semgrep comparison is the public semgrep-rules pack, 2,193 YAML files, not a proprietary engine or a customer-written pack
  • Language is inferred from file extensions
  • Repeat-finding counts match on file and CWE, which undercounts the same defect reported at a different location and overcounts unrelated defects that share both
  • Precision is our own reviewer triage of Opus and Mythos on a single codebase, not a vendor benchmark. Codex was never triaged
  • One estate, four codebases, three harnesses. Coverage of the broader repository estate is about 5.5 percent

Implications and future work

AI reads code well enough to find missing authorization, tenant isolation failures, object-level access bugs, RBAC ordering mistakes and time-of-check races that no rule-based tool in this estate had reported. That much is clear from this corpus. What this corpus does not show is how to make that detection operable: reproducible enough to manage, attributed to the right model for the class, normalized enough to measure, substantiated enough to act on, and fast enough to gate a merge. Those are properties of the analysis system, not of the model.

What we will measure next: whether class-level stability holds on other architectures, whether a validator whose job is to disprove a finding compresses the 99 minute review, and whether scoped passes can move this machinery into a merge gate without inheriting the recall gaps of a single opaque run. Cost, deployment and the shape of that system belong in a later note.