The Five Scanning Surfaces
Every production system exposes multiple attack surfaces, and no single scanning method covers all of them. Effective vulnerability scanning requires deliberate coverage across five distinct areas: source code, software dependencies, container and infrastructure configuration, running application behavior, and network-exposed services. Each surface has different characteristics that determine which scanning technique applies and what that technique cannot see.
The distinction between surfaces is not academic. An injection vulnerability in your application code requires a source code scanner to detect before deployment, but its actual exploitability from an attacker's perspective can only be confirmed by testing the running application. A vulnerable dependency version shows up in a manifest file but reveals nothing about whether the vulnerable function is actually called in your code paths. A misconfigured S3 bucket is invisible to code scanning because it exists only in infrastructure state. Treating "vulnerability scanning" as a single activity produces gaps that attackers routinely exploit.
The table below summarizes what each surface covers, the primary tool type that addresses it, and the fundamental limitation of that approach.
| Surface | Primary tool type | What it finds | Key limitation |
|---|---|---|---|
| Source code | Static analysis (SAST) | Logic flaws, injection patterns, hardcoded secrets, insecure API usage | Cannot confirm runtime exploitability; prone to false positives on complex data flows |
| Dependencies | Software composition analysis (SCA) | Known CVEs in third-party packages, license violations, outdated versions | Cannot determine whether the vulnerable function is reachable in your code |
| Containers and infrastructure | Image scanning, IaC analysis | OS package CVEs, base image vulnerabilities, misconfigured cloud resources | Point-in-time only; misses drift between scans and runtime configuration changes |
| Running application | Dynamic analysis (DAST) | Confirmed exploitable web vulnerabilities: XSS, injection, auth bypasses, SSRF | Requires a deployed environment; limited depth on authenticated or complex application flows |
| Network services | Network vulnerability scanner | Open ports, outdated service versions, default credentials, TLS misconfigurations | Noisy; many findings require manual validation to confirm exploitability |
How Source Code Scanning Works
Source code scanning, commonly called static application security testing or SAST, analyzes code without executing it. The scanner parses source files into an abstract representation, then applies rules that look for patterns associated with known vulnerability classes: user-controlled input flowing into a SQL query, a cryptographic function called with an insecure algorithm, a file path constructed from request parameters without sanitization.
The primary advantage of source code scanning is that it runs early in the development cycle, before code is deployed anywhere. Catching an injection vulnerability at pull request time costs a fraction of what it costs to find the same issue in production. Pipeline integration means every change is checked automatically without requiring developer action beyond writing the code.
The fundamental limitation is false positive rate. Static analysis does not execute code, so it cannot follow data flows through external systems, cannot account for runtime type coercion, and cannot distinguish between user-controlled input and internally-generated values in all cases. A scanner may flag a code path as potentially vulnerable when the input that reaches it is always sanitized upstream. Developers who encounter too many false positives start ignoring findings, which defeats the purpose of the tool.
More sophisticated source code scanners use taint analysis and data flow graphs to trace how values move through the code, reducing false positives by confirming that a potentially dangerous sink is actually reachable from a user-controlled source. This approach substantially improves accuracy but requires deeper analysis and longer scan times. The tradeoff between scan speed and finding quality is a practical consideration for teams running scans in pull request pipelines where developer wait time matters.
Source code scanning is also the right place to catch secrets and credentials that have been committed to the repository: API keys, private tokens, database passwords embedded in configuration files. This category of finding has near-zero false positive rate and near-immediate exploitability if the credentials are still valid, making it one of the highest-value things any source code scanner can detect.
How Dependency and SCA Scanning Works
Software composition analysis operates on the manifest files that describe what third-party packages your application uses: package.json, requirements.txt, go.sum, pom.xml, and their equivalents across ecosystems. The scanner resolves the full dependency tree including transitive dependencies and compares each package version against vulnerability databases to surface known CVEs.
SCA scanning is operationally simple to deploy and produces findings with high confidence at the package identification layer. If a package version is listed as vulnerable, it is vulnerable. The ambiguity lies in whether that vulnerability matters for your use of the package. A vulnerable function that is never called in your code paths poses no practical risk, but a basic SCA scanner has no way to make that determination. The result is a list of CVEs that requires significant manual triage to prioritize correctly. A CVSS 7.5 ReDoS in NLTK’s Text.findall() (writeup) is exactly the class of finding SCA tools surface — a vulnerable function in a widely used library that went unnoticed because the exploit requires a specific input pattern. Additional examples of dependency and application vulnerabilities found in production open-source projects are collected in Kira’s case studies.
For a detailed breakdown of how SCA fits alongside static and dynamic analysis methods, see the article on SAST, DAST, and SCA. The interaction between these three approaches matters because each one covers what the others miss, and understanding those boundaries is what allows teams to build complementary coverage rather than redundant coverage of the same surface.
Supply chain risk has made SCA scanning increasingly important beyond CVE detection. Packages that have been compromised at the registry level, typosquatted packages that mimic popular libraries, and packages with malicious code introduced through a compromised maintainer account all represent threats that a CVE database will not capture until after the fact. Some SCA tooling now includes behavioral analysis of package code and reputation scoring based on maintainer patterns, though this area of the discipline is still maturing.
How Container and Infrastructure Scanning Works
Container image scanning inspects the layers of a Docker or OCI image to inventory the OS packages installed, then compares that inventory against CVE databases. The scan covers both the base image and everything added in subsequent layers. The output is a list of vulnerable package versions that need to be updated, which typically means rebuilding the image with a newer base or patching specific packages.
The operational challenge with container scanning is that it is inherently point-in-time. An image that scans clean today may be vulnerable tomorrow when a new advisory is published for a package it contains. Base image update workflows need to be automated to ensure new advisories trigger image rebuilds rather than accumulating silently. Many teams scan images at build time but do not re-scan the registry on a schedule, creating a gap where deployed images become progressively more vulnerable over their lifecycle.
Infrastructure as code analysis applies static analysis to Terraform, CloudFormation, Kubernetes manifests, and similar configuration files. It looks for misconfigurations that create security risk: public-facing storage buckets, security groups that allow unrestricted inbound traffic, IAM roles with overly broad permissions, secrets stored in plaintext environment variables. This category of finding is structural rather than CVE-based and requires domain knowledge of the specific cloud provider's security model to interpret correctly.
Cloud configuration scanning against live environments adds a layer that IaC analysis cannot provide: it detects drift between the declared infrastructure and the actual running state, and surfaces resources that were created outside the IaC workflow entirely. Shadow infrastructure, resources spun up manually for temporary purposes and never cleaned up, represents a significant category of real-world cloud exposure.
How Runtime and DAST Scanning Works
Dynamic application security testing interacts with a running application the way an attacker would: by sending crafted HTTP requests and analyzing the responses. DAST does not read source code. It discovers endpoints by crawling the application, then systematically attempts to trigger vulnerability classes by injecting payloads designed to produce observable effects: SQL errors, reflected script execution, server-side request forgery responses, authentication bypasses.
The key advantage of dynamic scanning is that findings are confirmed against the actual running application. A DAST scanner that successfully extracts data through a SQL injection payload has demonstrated that the vulnerability is exploitable, not just that the pattern exists in source code. This dramatically reduces the triage burden compared to static analysis output, because false positives require the scanner to have produced a false response from the application, which is rare for well-implemented checks.
The limitation is coverage. DAST is most effective against applications with accessible HTTP endpoints that do not require complex authentication flows. Endpoints behind multi-factor authentication, application workflows that require specific session state, or functionality that depends on prior user actions can be difficult or impossible for a crawler-based scanner to reach. The depth of coverage is directly proportional to how well the scanner understands the application's structure, and for complex modern applications that structure is often not fully discoverable through automated crawling.
Runtime application self-protection is a related technique that instruments the running application from the inside rather than probing it from the outside. RASP can detect and block attacks at the moment they are attempted, with visibility into the actual code path being executed. It represents a complementary approach to external DAST rather than a replacement, since it requires instrumentation of the application runtime and adds overhead to production execution.
Choosing the Right Coverage for Your Stack
The practical question for most engineering teams is not which scanning method is theoretically best but which combination of methods provides the most coverage for their specific stack given their operational constraints.
For teams just starting to build scanning coverage, the highest-value first step is almost always source code scanning integrated into CI/CD. It runs on every code change, catches issues at the lowest remediation cost, and produces findings that developers can act on immediately. Secrets detection is included in most source code scanners and should be enabled from day one.
Dependency scanning is a close second priority. The manifest files already exist in the repository, scanning them requires minimal setup, and the findings are high-confidence at the package version level even if reachability analysis requires additional tooling. Most package ecosystems now have advisories published within hours of a CVE being assigned, making this the fastest-moving source of new findings in most stacks.
Dynamic scanning becomes high priority once there is a stable deployed environment to test against. For web-facing applications, DAST provides the confirmation layer that converts static analysis findings from "possibly vulnerable" to "confirmed exploitable." Kira combines multi-surface scanning with exploit validation to deliver confirmed findings across code, dependencies, and application behavior, reducing the triage overhead that comes from managing findings across disconnected tools.
Container and infrastructure scanning should be added once the CI/CD and dependency scanning loops are working reliably. These surfaces tend to produce higher-volume, noisier output that requires more triage infrastructure to manage. Adding them before triage processes are in place often results in a backlog that overwhelms the remediation workflow.
The goal is not to run every possible scanner. It is to build a closed loop where findings are consistently discovered, triaged, assigned, and resolved. For more on building that loop as a program rather than a collection of tools, see the article on vulnerability management programs. For the organizational context of where scanning fits within a broader application security discipline, see the overview of application security programs.
FAQ
How often should I run vulnerability scans on production systems?
The right frequency depends on the scanning method and how fast your environment changes. Source code scans should run on every pull request and every merge to the main branch, since the cost of scanning is low and the value of catching issues before they ship is high. Dependency scans should run both in the pipeline and on a daily schedule against production manifests, because new advisories are published continuously regardless of whether your code changes. Container image scans should run at build time and on a regular schedule against images in your registry, since base image vulnerabilities arrive on the advisory schedule rather than your build schedule. Infrastructure scans against live cloud environments typically run daily to weekly depending on how frequently your infrastructure changes. DAST scans against running applications are often run weekly or on-demand after significant changes, because they require more setup and produce findings that take longer to investigate. The general principle is that the scan frequency should be fast enough that no significant change to the attack surface goes unscanned for more than the window during which an attacker could exploit it.
What is the difference between authenticated and unauthenticated vulnerability scans?
An unauthenticated scan tests only what an anonymous user can reach: public-facing endpoints, login pages, error messages, and anything else accessible without credentials. An authenticated scan provides the scanner with valid credentials so it can access the full application surface behind the login flow, including user-specific functionality, administrative interfaces, and API endpoints that require session tokens. The difference in coverage is substantial. Most real application functionality lives behind authentication, which means an unauthenticated scan misses the vast majority of potential injection points, broken access control issues, and business logic vulnerabilities. For meaningful DAST coverage of a production application, authenticated scanning is required. The practical challenge is managing credentials: the scanner needs valid accounts that do not interfere with real user data, and the credentials need to be rotated and managed securely within the scanning infrastructure. Some applications also rate-limit or lock accounts after repeated failed login attempts, which can cause unauthenticated probing to trigger account lockouts for real users if not handled carefully.
Can vulnerability scanning replace manual penetration testing?
Vulnerability scanning and manual penetration testing find fundamentally different categories of issues, and neither can replace the other. Automated scanners excel at systematic coverage: they check every endpoint for known vulnerability patterns, never forget to test a specific input field, and can process large applications in a fraction of the time a human tester could. What scanners cannot do is chain vulnerabilities together creatively, understand business logic well enough to identify authorization flaws that require understanding the intended behavior, or find novel attack paths that do not match any existing rule. A manual penetration tester brings adversarial reasoning: they ask what would happen if a regular user account could access admin functionality, or what the business impact of a race condition in a payment flow would be, or how an exposed internal API endpoint could be pivoted into a deeper compromise. The practical approach for most organizations is to use automated scanning continuously to maintain a baseline of known-vulnerability coverage and to run manual penetration tests at least annually or after significant architectural changes, using the scan results as context for the tester rather than a substitute for their work. Organizations with higher risk profiles or compliance requirements typically run penetration tests more frequently and scope them specifically to the areas where business logic complexity is highest.