White paper ·

SAST and SCA: Nothing Matched the Patterns We Know About

What each discipline reads, what neither of them proves, and why generated code changed the arithmetic

Part 1 of 3 in Security Scanning and the AI Codebase. Each piece stands on its own. The others: Part 2 · Part 3.

Contents7 sections

Forty-three findings nobody introduced

The first full scan of this project's tree came back with forty-three findings. Not one arrived that week. They had been sitting in the repository for as long as the repository had existed, and the only thing that changed on the day the number appeared was that a tool with no opinion about any of it finally read the files end to end. Nobody had introduced anything.

That number is the useful part of the exercise, and it is useful in a way nobody expects going in. Forty-three measures elapsed neglect. It says how long the code went unread by a machine that reads for a living, and it says almost nothing about how exposed anybody was. Turn the same scanner on any tree of comparable age and the number lands in the same neighbourhood, which is why a first scan is a baseline rather than a verdict.

The mistake worth naming runs the other way. A clean report gets read as a statement about the software; what it actually states is something about the scanner. Nothing matched the patterns we know about, in the code we could see. That sentence is real and useful, and it is a great deal smaller than the sentence people hear when somebody says the scan came back clean.

Getting that one sentence written down, in the place where release decisions actually get made, is most of what a scanning programme is for. The rest is plumbing.

Two acronyms, two different corpora

SAST and SCA get used as though they were one purchase. They read different material and answer different questions. That difference decides who fixes a finding.

Static Application Security Testing, SAST from here on, reads the source your team wrote without running it, hunting for shapes that stay dangerous whatever the input reaching them turns out to be. A shell command assembled by string concatenation is one. A hash chosen for speed where resistance was the point is another. Static is the operative word: no test data, no deployment, no traffic.

Software Composition Analysis, SCA, reads the dependency tree instead. It takes everything you imported plus everything those imports pulled in, resolves the lot to exact versions, and checks the result against public advisory databases. That is a lookup rather than an analysis. SCA never reads the dependency's code.

Secret scanning is the third discipline, routinely folded into the other two and covered by neither. It reads the diff and the history for credentials. SAST hunts unsafe patterns and sails straight past a pasted API key. Worse, once a key reaches the history, deleting the line leaves it exactly where it was.

What each discipline reads, and who ends up owning the fix.
Figure 1. What each discipline reads, and who ends up owning the fix.

Ownership follows the corpus. A SAST finding is your code and your patch. An SCA finding is usually a version bump, and occasionally a long, unrewarding conversation with an upstream maintainer who stopped caring about that package two years ago and has no plans to start again.

Two properties are worth stating before anybody buys anything, because most disappointment with these tools traces back to one of them.

Neither runs your program. Both are blind to runtime configuration; both are silent on whether a finding is reachable in production. Penetration testing is a different discipline and these are not it.

Neither proves absence either, which is the sentence from the opening.

What each one sees, and the list nobody owns

Coverage reads best as three lists. What SAST sees, what SCA sees, and the one everybody forgets to write down.

Finding classWhat it looks likeWho sees it
Command or SQL injectionA shell string built by concatenationSAST
Weak or misused cryptoMD5 for integrity, verify=FalseSAST
Unsafe deserializationpickle.loads, yaml.load with no SafeLoaderSAST
Dangerous defaultsBound to 0.0.0.0, debug on in productionSAST
Known CVE, direct dependencyThe version you pinned is listedSCA
Known CVE, transitiveFour levels down, nobody chose itSCA
Typosquats and malicious packagesExists only to be installedSCA
Licence incompatibilityGPL in a product meant to stay closedSCA
A credential in a diff or in historyNeither SAST nor SCA reads for thisSecret scanning
Broken authorizationThe check is present, and wrongNobody
Business-logic flawsRefund twice, negative quantityNobody
A vulnerability nobody has publishedSCA knows advisories, not codeNobody

Table 1. The last three rows are why a green scan is not a safe release.

SAST is strong on local, syntactic danger: the pattern sits in a few lines and never depends on program state. Context is where it weakens. A false positive rate that feels intolerable is usually the tool telling the truth about a construct that is dangerous in general and safe right here, with no way on earth to know which of the two it is looking at. It cannot see that the string reaching your SQL is a UUID your framework validated four layers up. That is the trade. It errs toward flagging, and a team that never triages ends up ignoring the whole thing.

SCA is close to unarguable on facts: this version, this advisory, published on this date. Its weaknesses are the shape of its data rather than a defect in the tool. It knows only what has been published, so a vulnerability discovered and not yet disclosed is invisible to it; SCA is a memory rather than a detector. Most CVEs in a large tree also sit in code paths your application never calls, and that gap between present and reachable is what VEX statements exist to record.

Then comes the list nobody owns: broken authorization, business-logic flaws, and anything not yet published. No scanner finds these. Review, tests and threat modelling do, which is an argument for scanners rather than against them, since they clear the mechanical findings so that human attention reaches the ones only a human was ever going to catch.

The family these two belong to

A programme that adopts SAST and SCA and stops has done real work. It has also covered less of the surface than it feels like. These two are the cheapest members of a larger family; cheap is why they get adopted first. Seeing the whole set once is worth the ten minutes, if only to know what you are choosing to skip.

DisciplineWhat it readsWhat it sees that the others do notIn this piece
SASTYour source, unexecutedInjection, weak crypto, unsafe deserializationIn scope
SCAYour resolved dependency treePublished advisories against the versions you pinnedIn scope
Secret scanningThe diff, and the whole historyCredentials, which neither of the two above read forIn scope
Infrastructure as codeTerraform, Helm, CloudFormationA public bucket, an open security group, a role granted everythingNot covered
Container scanningThe built image, layer by layerThe operating system packages your lockfile never mentionsNot covered
DASTThe running application, over HTTPWhat is actually exposed once configuration and routing are realNot covered
IASTThe application from inside, under test trafficReachability, which SAST and SCA both have to guess atNot covered
FuzzingThe application under hostile generated inputCrashes and memory faults nobody wrote a test case forNot covered

Table 2. Ordered by how much has to exist before the scanner can run. SAST needs a file; DAST needs a deployment.

Two of those deserve singling out, because they are commonly the highest-yield thing a team is not doing. Infrastructure-as-code scanning reads Terraform, Helm and CloudFormation. In a cloud-hosted product, the misconfiguration it finds is routinely a shorter path to your data than any code defect in the application, for the unglamorous reason that a storage bucket readable by the world needs no injection flaw standing in front of it to be useful to somebody. That scanner is cheap and static, and it fits the same slot in continuous integration.

Container scanning reads the built image, which carries an operating system your lockfile has never mentioned. A Python dependency audit says nothing whatever about the base image's OpenSSL. That base image ships to production either way.

Neither is covered here. Both sit closer to SCA in cost than to DAST. If you are choosing where the second increment of effort goes, they are the honest answer more often than configuring SAST more thoroughly is.

Why the arithmetic changed

None of the above is new. What changed is the ratio between how fast code is produced and how fast it gets examined, and every argument for automating security review is really an argument about that ratio.

Review capacity stayed where it was. Generation did not. A reviewer reads roughly the same number of lines per hour as five years ago, so when a session produces a day of code in an hour the constraint moves to review, and the only checks that moved with it are the mechanical ones.

Generated code is idiomatic-looking, which is exactly the failure mode reviewers handle worst. Insecure code written by a person in a hurry tends to look hurried. Model-written insecure code looks like the rest of the file, because plausibility is the thing the model optimises for. Nothing about it trips the instinct that makes a reviewer slow down and read twice.

Training data has a half-life. A model suggests the API it saw most often, and the most-seen API is frequently the older one. yaml.load over safe_load. MD5, from a decade when nobody minded. verify=False, from ten years of tutorials working around a certificate error. Those are precisely SAST's strongest category, which is a piece of luck worth taking.

Dependencies now get suggested rather than chosen. An import can arrive on plausibility alone, with nobody having evaluated anything; models also suggest packages that have never existed. Attackers register those names. Vigilance makes a poor defence here. A lockfile plus SCA makes a good one, because SCA asks about the package you actually resolved rather than the one you meant to.

One mistake also replicates now. A pattern applied by hand appears once. Applied by an agent across a refactor it appears in thirty files in a single commit, and what the reviewer opens is a large diff of consistent-looking changes: the hardest kind to read carefully, and the easiest kind to approve.

The conclusion concerns pace rather than danger. That cheap mechanical layer of review has to be genuinely automatic now, because it is the only layer keeping up. The same velocity cuts the other way, and that half rarely gets said: a finding a model can fix in seconds is a finding worth surfacing, and backlogs that were previously untouchable become tractable. Scanners produce exactly the kind of work these tools are unusually good at doing.

What you are actually deciding

Reduced to the decision rather than the tooling, five things.

Adopt both, early. They are cheap, they run on a laptop, and their strongest category is precisely where generated code most often lands. The cost is triage rather than licence fees.

Budget for triage, not just for scanning. An unfiltered report is not a work queue. Programmes do not fail at installation; they fail when a queue nobody can finish gets ignored, then switched off.

Baseline the existing debt. A gate that is red on arrival gets bypassed, and a bypassed gate is worse than an absent one because it still reports. Record what is already there. Fail on additions.

Do not let green mean safe. Write the sentence down once, in the place where release decisions get made. A clean scan means nothing matched the patterns we know about.

Cost the next increment honestly. The second-best spend after these two is usually infrastructure-as-code or container scanning. Both are static, both are cheap, and both cover ground neither of these tools can reach.

SAST reads what you wrote. SCA reads what you imported. Secret scanning reads what should be in neither. None of the three sees your logic, your authorization, or tomorrow's disclosure, and in a system with a model inside it none of them sees the model at all. Adopt them anyway, because in a codebase where most lines are now drafted faster than anyone can read them, that mechanical layer is the part of review still keeping pace. Then go and look at the forty-three findings that were always there.

Glossary

TermDefinition
Advisory databaseA public record of disclosed vulnerabilities, each tied to a package and a version range. SCA is a lookup against one of these, which is why it knows only what somebody has already published.
BaselineA recorded set of findings that already existed when scanning started, held so that a gate can fail on additions rather than on history. Without one, the first scan turns every pull request red.
CVECommon Vulnerabilities and Exposures, the identifier scheme that gives a disclosed vulnerability a stable name. It says a flaw exists in a version; it says nothing about whether your application reaches it.
DASTDynamic Application Security Testing. It exercises the running application over HTTP, so it sees what configuration and routing actually expose, and it needs a deployment before it can run at all.
FuzzingFeeding an application generated hostile input to find crashes and memory faults. It finds defects nobody wrote a test case for, at the cost of a rig and a great deal of machine time.
IASTInteractive Application Security Testing. It instruments the application from inside while test traffic runs, which lets it answer the reachability question that SAST and SCA both have to guess at.
Infrastructure as codeDeployment configuration kept in files such as Terraform, Helm or CloudFormation. Scanning it is cheap and static, and in a cloud product it often finds a shorter path to the data than any application defect.
LockfileA file recording the exact resolved version of every dependency, direct and transitive. It is what turns a loose version range into a fact that SCA can check.
ReachabilityWhether your code can actually call the vulnerable path in a dependency. Most CVEs in a large tree sit in paths the application never touches, which is the difference between present and exploitable.
SASTStatic Application Security Testing. It reads the source your team wrote without running it, looking for shapes that are dangerous whatever the input.
SCA. Software Composition AnalysisIt resolves the dependency tree to exact versions and asks whether anyone has published a vulnerability against them.
Secret scanningReading a diff and a repository's history for credentials. It is a separate discipline from the other two, and it is the only one that treats history as part of the corpus.
Transitive dependencyA package you did not import, pulled in by something you did. Most of a large tree is transitive, and most CVEs land there.
TriageThe work of turning a raw scan report into a queue somebody can finish. It is the real cost of a scanning programme, and the place programmes die.
VEXVulnerability Exploitability eXchange, a format for recording that a known vulnerability is present in a product but not exploitable in it. It exists so that a reachability judgment survives the analyst who made it.
Agentic AIWPR-AGT-004
Hashtags

About the author

Jason Newell leads organizations and people in technology, and is a researcher, deep technologist and builder — from a CRAY to agentic AI. He founded MAX Research Collective to build AI for regulated industries that runs where its owner has control, with healthcare as its strongest focus. About Jason → Connect on LinkedIn →

Free to read, share and quote: credit Jason Newell, MAX Research Collective, and link to this article’s original page, https://www.maxresearchcollective.com/articles/sast-and-sca-patterns-we-know-about.html. License & copyright →

Part 1 of 3 · Security Scanning and the AI Codebase

The rest of the set

Three pieces that each stand on their own and read end to end: what the scanners read and cannot prove, where to run them so the gate survives, and the four layers of an AI product they never see.

Related

Related field notes

Keep reading

More field notes

This piece is part of the MAX Research Collective library. Browse the rest, or connect on LinkedIn.

Follow the collective

New research, projects and field notes as they publish. Following happens on LinkedIn — we never see an address, and there is nothing to unsubscribe from.

Or get it by email

An occasional summary of new research, each with a link to the full piece.