Code scanning tools — SAST code scanning, secret scanners, and linters — fall into three categories that get conflated constantly — SAST for security vulnerability patterns, secret scanners for hardcoded credentials, and linters for style and correctness bugs — and each one misses what the other two catch, so running only one leaves real gaps. A team that runs ESLint and calls it "code scanning" has covered maybe a third of what a static analysis pipeline should cover. Understanding the boundaries between these three categories is the difference between a pipeline that actually blocks bad code and one that gives false confidence.
What does a SAST tool actually catch that a linter doesn't?
SAST (static application security testing) tools trace data flow from untrusted input sources to dangerous sinks — a user-controlled request parameter reaching a raw SQL query, an unsanitized string reaching an eval() call, a file path built from user input reaching a filesystem read. Linters like ESLint, Pylint, or RuboCop check syntax, style, and common correctness bugs (unused variables, unreachable code, type mismatches) but generally don't do the cross-function data-flow tracing that catches injection vulnerabilities. Some linter rule sets (like eslint-plugin-security) flag a subset of risky patterns, but they're pattern-matching on syntax, not tracing real data flow the way a proper SAST engine does — which is why linter-only setups miss most of the OWASP Top 10 categories.
Why do you need a separate secret scanner if SAST already scans the code?
Secret scanners look for a specific pattern class — API keys, private keys, database credentials, cloud access tokens — using regex and entropy analysis across the entire git history, not just the current file state. A SAST engine analyzing control flow generally isn't built to notice that a string literal sk_live_51H... matches a Stripe key format; that's a different detection technique entirely (pattern/entropy matching versus data-flow analysis). And secrets are a uniquely durable problem: once a credential is committed, it exists in every clone of the repo's history until rotated, regardless of whether the current HEAD still contains it — which is why secret scanners need to scan full git history, not just diffs.
Can linters catch security bugs at all?
Some, but only a narrow slice. Rule sets like eslint-plugin-security, bandit for Python, or gosec for Go bolt security-adjacent pattern checks onto a linter framework, flagging things like use of pickle.loads on untrusted data or hardcoded subprocess calls with shell=True. These are useful as a first layer because they run fast and integrate into the same editor feedback loop developers already use for style. But they generally don't do cross-file taint tracking, so a vulnerability that requires following user input through three function calls and a class method will pass a linter and get caught by a real SAST engine.
How should these three tools actually be stacked in a pipeline?
Run linters on every save or pre-commit hook for immediate feedback, run secret scanning on every commit and as a full-history one-time sweep when adopting a new tool, and run SAST on every pull request with a merge gate on new critical/high findings. This order matches feedback latency to risk: linter feedback needs to be instant because developers are watching it live; SAST needs to complete before merge but can take a few minutes; secret scanning needs full history coverage at least once, then incremental checks per commit after that. Layering them this way — rather than picking one tool and hoping it covers everything — is standard in mature code scanning tools pipelines and is what most CI-integrated platforms, including Safeguard's SAST/DAST product, are built to support out of the box.
What's the actual overlap between these categories?
Less than you'd think. A 2023-era internal comparison across common open-source projects generally shows less than 10% overlap in findings between a dedicated SAST engine and a security-flavored linter ruleset — they're catching different bug classes almost entirely. Secret scanners have essentially zero overlap with either, since they're not analyzing logic at all. The practical takeaway: none of the three is a substitute for the others, and "we run a linter" is not the same claim as "we run SAST."
FAQ
Are SAST scanning tools the same as linters with extra rules?
No. SAST scanning tools trace data flow across functions and files (taint tracking from an untrusted source to a dangerous sink); linters, even security-flavored ones, mostly pattern-match within a single file or function. That difference is why SAST code scanning catches vulnerability classes a linter architecturally can't see.
Do I need all three code scanning tools, or can I start with one?
Start with a linter (cheapest, fastest feedback) and a secret scanner (cheap, catches a uniquely dangerous and durable bug class), then add SAST once you have CI budget for the longer scan time. Skipping SAST entirely leaves the OWASP-style injection and logic bugs uncaught.
Is Semgrep a SAST tool or a linter?
Semgrep sits in between — it's rule-based like a linter but supports enough cross-line pattern matching to catch many security patterns, though it doesn't do the full interprocedural taint tracking that a heavier SAST engine like Checkmarx does. Many teams run both.
How does this fit into SCA and dependency scanning?
Separately. Code scanning tools analyze your own source code; SCA analyzes your open-source dependencies for known CVEs. You need both, since most production codebases are majority third-party code.
Where do these tools fit relative to SAST and DAST?
Code scanning tools (SAST, linters, secrets) run against source before it ships; DAST tests the running application. See our SAST vs DAST breakdown for how the two stages complement each other, and compare vendors if you're evaluating platforms.