24 August 2026

SAST and Business Logic Vulnerabilities: What Static Analysis Can Reach

SAST and Business Logic Vulnerabilities: What Static Analysis Can Reach
10 min. readBy Borg

If you are looking for a static analyser that catches business logic vulnerabilities, it is worth knowing up front that this is a fit problem rather than a shortlist problem. Static analysis is built to recognise patterns in source code. Most business logic bugs are not patterns. They are an application behaving exactly as written, where what was written was incomplete.

That does not make static analysis the wrong purchase. It makes it the wrong instrument for this particular class of bug. So the useful version of this page is not a ranked list of products. It is which part of the problem a static analyser can reasonably be expected to cover, what the published accuracy research suggests you should expect from it, and what tends to cover the rest.

Everything below compares categories of tooling rather than named products.

What static analysis genuinely covers

Start with the case for it, because it is a real one and it tends to get undersold by people selling something adjacent.

A static analyser examines software artefacts without executing the application, looking for code patterns, data flows, configurations, or other properties associated with known weaknesses. A credential committed to a configuration file. User input reaching a query without parameterisation. A deserialisation call on untrusted input. A declared dependency with a known advisory against it. These are the kinds of weaknesses static analysis is well suited to finding because they can be expressed in rules that a tool can evaluate automatically.

It is also cheap to place. Static analysis can be integrated into CI/CD so that scans run automatically on commits or pull requests, allowing many of the weaknesses it covers to be identified close to when they are introduced. That is worth a lot, and offensive testing does not replace it.

The useful framing is that static analysis is particularly effective when a vulnerability can be expressed in terms of recognisable code properties, data flows, control flows, or other rules that can be evaluated from the software being analysed. Business logic asks a different question, and the rest of this page is about why that difference matters more than it sounds.

Why business logic sits outside the pattern

Business logic vulnerabilities often arise because an application's implementation does not correctly enforce an intended rule or invariant. Sometimes that means a missing check; in other cases it is an incorrect condition, state transition, calculation, or authorisation decision. The code can behave exactly as implemented while still violating a rule the application is supposed to enforce.

Take a handler that fetches an order by its identifier. The query is parameterised, the input is validated, there is no injection anywhere near it. If the handler never confirms that the requesting account owns that order, the endpoint returns another customer's data. There may be no generic code pattern that tells an analyser that the missing ownership check is a vulnerability. A tool can detect this kind of issue when the relevant authorisation invariant and application structure are sufficiently explicit for its rules or analysis to reason about them.

The same shape recurs across the classes that tend to matter most in multi-tenant software. Whether organisation A can reach organisation B's records depends on how your particular tenancy model is enforced across your particular service boundaries. Whether a checkout can reach a paid state without a payment event depends on your particular state machine. A static analyser can reason about application-specific invariants when those invariants are represented in code, configuration, annotations, specifications, or custom rules it understands. In many applications, however, important business rules are only partially or implicitly represented in those forms.

Some analysers do better here than others. Tools that let you author custom rules can encode some of your own invariants, and that is worth doing for the ones you can express cleanly. That approach tends not to scale to the full set. The methodology for finding the rest is a separate exercise, covered in how to test business logic vulnerabilities.

What the published accuracy research suggests

Alert fatigue gets asserted often and measured rarely. A few third-party figures are worth knowing, and they are more useful than any vendor's own numbers.

NIST's SATE V evaluation found substantial variation in static-analysis accuracy. In its synthetic Java track, the tools evaluated had precision ranging from 78% to 94%, with an average precision of 85%. NIST also noted that higher recall can come with lower precision, illustrating the trade-off between finding more potential weaknesses and producing more incorrect warnings.

The OWASP Benchmark measures true-positive and false-positive rates separately and publishes results for individual tools. On the Java v1.2 benchmark, FindSecBugs v1.4.6 reported a 96.84% true-positive rate and a 57.74% false-positive rate. Historical commercial-tool results published for the Java v1.1 benchmark show six anonymous SAST tools with true-positive rates ranging from 28.96% to 85.02% and false-positive rates ranging from 12.22% to 52.09%. These figures illustrate why benchmark results should not be treated as universal production accuracy figures: the relationship between recall and false positives varies substantially by tool and benchmark.

Read together, those results suggest that the practical constraint is often not how many issue types a scanner can detect, but how many findings a team can realistically investigate. For a small team, triage capacity can matter more than scanner count.

The same results suggest a selection criterion that is more useful than a feature list. The number worth asking a vendor about is not how many issue types are detected, but how many findings a developer reads before reaching one that is real in your codebase.

Where each approach sits

ApproachWhat it covers wellWhere it stops
Static analysisKnown-dangerous constructs in source, cheap enough to run on every commitReads code without executing it, so a missing check is hard to distinguish from a check that was never needed
Software composition analysisKnown advisories against declared dependenciesSays less about whether the vulnerable path is reachable in your application
Dynamic scanningBehaviour of the running application against generic payloadsCoverage depends heavily on how the application is discovered and exercised; routes, states, authentication contexts, and multi-step workflows that are not reached by the test configuration may remain untested
Custom static rulesInvariants you can express as a patternCovers only the invariants somebody wrote down
Offensive testing with code contextAttempting the action and observing the result, including cross-tenant and multi-step casesReasons from the repository and what is reachable at runtime, so anything configured outside those sources can fall outside its view

None of those rows is a criticism of the others, and most teams past a certain size run several. They answer different questions. Where the feedback lands also differs, which is the subject of full pentest vs PR security review.

Choosing a static analyser well

If a static analyser is what you are buying, the questions that separate them are narrower than the feature lists suggest.

How much noise reaches a developer? Ask for a false-positive rate on a codebase resembling yours rather than on a benchmark suite. The figures above suggest the honest answer varies considerably.

Can it express your own rules? The invariants you can encode as a pattern are the slice of your business logic a static tool can help with. If custom rules are awkward to author, that slice is smaller in practice than on paper.

Does it distinguish reachable from unreachable? Some analysers can say whether a matched pattern is reachable from an entry point at all. That distinction tends to do more for triage load than almost anything else on a datasheet.

Where do findings land? A finding in a portal nobody opens is not much better than a report nobody reads.

None of those questions is about business logic, which is rather the point. Choose the static analyser on what static analysis is for, and treat the logic layer as a separate line item.

What tends to cover the rest

The part of the logic problem that automation takes over is the systematic part. Enumerating every endpoint that resolves a record by a caller-supplied identifier, checking which of them carry an ownership check, and re-running that after each change is patient work rather than creative work.

That is where Borg's Mjolnir fits. It reads the repository to decide which paths are worth attacking, executes against the running application, reports findings with reproduction steps, and retests once a fix lands. Gungnir applies a narrower version of the same approach to a pull request diff, before the change reaches production. Huginn tracks the external attack surface as it changes. Findings collect in Odin, the dashboard where Mjolnir, Gungnir and Huginn report their results. There is a fuller description in what continuous pentesting means.

The limit belongs in the same paragraph. Code-aware testing is constrained by the context available to it. Configuration that exists only in a cloud console or third-party dashboard, external services whose behaviour is not represented in the repository, and business rules that exist only as undocumented assumptions may require additional human or specialised testing. It also does not stand in for a researcher reasoning about an unusual architecture.

A sequence that tends to work

If the question behind the search is really what to run and in what order, the sequencing below is a reasonable default. It is ordered by how much you get for the effort rather than by how much ground each one covers.

Dependency advisories first. Software composition analysis is relatively cheap to automate, and a published advisory provides a concrete issue to investigate. Whether that vulnerability is actually reachable or exploitable in your application can still require additional analysis, but the triage cost is low compared with almost anything else.

Then static analysis, tuned rather than maximal. Turn on the rule classes with a low false-positive rate for your stack and leave the rest off until someone has capacity to triage them. A scanner configured for maximum coverage can create a large triage burden for a small team. In practice, a narrower configuration that produces findings developers can consistently review may be more useful than one that generates more alerts than the team can process.

Then write down your invariants, whether or not a tool can check them. The list of things that should never be possible in your application is useful even as a document. It tells a reviewer what to look for, it tells a new engineer what the rules are, and where an invariant happens to be expressible as a pattern you can hand it to a custom rule.

Then cover the logic layer with something that attempts the action. For many logic flaws, source analysis alone does not answer the final question: whether the prohibited action actually succeeds in the deployed application.

What that ordering deliberately avoids is buying breadth before triage capacity. If a new security layer produces more findings than the team can realistically triage, adding still more tooling is unlikely to solve the underlying capacity problem.

Conclusion

The search that brings people to this question usually assumes the answer is a product name. The more useful answer is that static analysis and business logic are a poor fit by construction, and noticing that early can save a procurement cycle.

Run a static analyser, choose it on the things it is genuinely good at, and treat the logic layer as a separate problem with its own instrument. Borg tests that layer, auth flows, APIs, access control, tenant isolation and billing logic, continuously and with the repository as context, and delivers findings with reproduction steps into the tracker your engineers already use. Book a walkthrough to see what it turns up against your own application.

Frequently asked questions

Can a SAST tool find business logic vulnerabilities?
Some of them, in limited cases. Where an invariant can be expressed as a pattern, a custom rule can catch violations of it, and that is worth doing for the rules you can write cleanly. Many business logic vulnerabilities depend on application-specific rules, relationships, or state transitions that generic static analysis cannot fully represent. Choosing an analyser primarily on its ability to find business logic vulnerabilities is therefore likely to disappoint.
Why does static analysis miss IDOR?
Because there is often no generic code pattern that tells the analyser that an ownership check is required. The query can be parameterised and the input validated, and the endpoint can still return another customer's record if the handler never confirms ownership. The issue depends on an application-specific relationship between a user and a record, which generic static analysis may not have enough context to evaluate.
What false positive rate should I expect from static analysis?
It varies enough that a single number would mislead. NIST's SATE V reported precision ranging from 78% to 94% on its synthetic Java track and found substantial variation across production Java code as well. On the OWASP Java Benchmark, FindSecBugs v1.4.6 reported a 96.84% true-positive rate and a 57.74% false-positive rate, while six historical anonymous commercial tools ranged from 28.96% to 85.02% true-positive rate and 12.22% to 52.09% false-positive rate. The useful takeaway is that accuracy varies substantially by tool, benchmark, and codebase rather than following a single fixed rate.
Should I stop running static analysis if I have offensive testing?
No. They cover different defects. Static analysis is cheap to run on every commit and catches constructs with a known shape, such as a committed credential or an unparameterised query, close to when they are introduced. Offensive testing addresses the classes that depend on application-specific behaviour. Dropping the first to fund the second tends to reopen a gap that was already closed.
What should I actually compare when choosing a static analyser?
Four things tend to separate them: how many findings a developer reads before reaching a real one in your codebase, how easily you can author custom rules, whether it distinguishes a reachable match from an unreachable one, and where findings land in your workflow. Detected issue-type counts are easy to compare and tell you comparatively little.

Borg on
Social Media

Borg specializes in securing complex, high-risk digital systems.