Safeguard
AppSec

How to Scan Source Code for Vulnerabilities: A Practical Guide

Scanning source code means running automated analysis over your repository to find security flaws before they reach production. Here is how to do it well.

Priya Mehta
DevSecOps Engineer
7 min read

To scan source code for vulnerabilities you run automated analysis tools over your repository that read the code and its dependencies, flag known weaknesses, and report them before the code ships. That single sentence hides a lot of nuance, because there is no one tool that catches everything. A useful source code scan combines at least two techniques, runs on every change, and reports findings where developers already work.

This guide walks through what actually happens when you scan code for vulnerabilities, which tools cover which risks, and how to keep the signal high enough that people stop ignoring the results.

What a source code scan actually looks at

When people say they want to scan source code, they usually mean one of two very different things.

The first is analyzing the code your team wrote. Static Application Security Testing (SAST) parses your files, builds a model of how data flows through functions, and looks for patterns that lead to injection, path traversal, hardcoded secrets, or unsafe deserialization. It does not run the program. It reasons about it.

The second is analyzing the code your team did not write. Modern applications are mostly third-party dependencies, and a source code scan that ignores your package-lock.json or pom.xml misses the majority of your attack surface. Software Composition Analysis (SCA) resolves your dependency tree and matches each package and version against vulnerability databases.

A serious program does both. SAST finds the SQL injection you introduced last sprint; SCA finds the transitive Log4j-style dependency three levels down that you never chose directly.

The main scanning techniques, and what each one misses

No single technique is complete. Knowing the blind spots keeps you honest about coverage.

  • SAST reads source without executing it. Great for catching insecure patterns early, but it produces false positives because it cannot always tell whether a tainted value is actually reachable.
  • SCA matches dependencies against advisory feeds. Precise about what you depend on, but it flags known CVEs whether or not your code calls the vulnerable function.
  • Secret scanning greps for API keys, tokens, and private keys committed by accident. Cheap to run and worth doing on every push and in git history.
  • DAST tests a running application from the outside, which is not a source code scan at all but complements one by catching runtime and configuration issues SAST never sees.

If you only have budget for one to start, begin with SCA. Dependency vulnerabilities are the most common way real breaches happen, and the fix is usually a version bump rather than a code rewrite.

How to scan source code for vulnerabilities in practice

Here is a concrete starting point using open-source tooling, which you can run locally before you buy anything.

For a Node project, a dependency scan is one command:

npm audit --audit-level=high

For deeper SCA across ecosystems, Trivy scans a project directory and its lockfiles:

trivy fs --scanners vuln,secret --severity HIGH,CRITICAL .

For static analysis of your own code, Semgrep runs a curated ruleset:

semgrep --config auto --error .

Each of these can run on a laptop in seconds to minutes. The point of running them by hand first is to see the raw output volume before you inflict it on the whole team through CI.

Wiring scans into CI without slowing everyone down

A scan that only runs when someone remembers to run it protects nothing. The goal is to scan source code for vulnerabilities on every pull request, automatically, and to fail the build only on findings that matter.

A minimal GitHub Actions step looks like this:

- name: Dependency scan
  run: trivy fs --exit-code 1 --severity CRITICAL --scanners vuln .

Two design choices keep this from becoming noise. First, gate the build only on CRITICAL (and maybe HIGH) findings at the start, then tighten the threshold as your backlog shrinks. Second, scan on pull requests so findings show up as review comments on the diff that introduced them, not as a wall of alerts three weeks later.

For a broader look at where dependency risk comes from and how it moves through a build, our write-up on software composition analysis covers the transitive-dependency problem in more depth.

Cutting false positives so people keep reading the output

The fastest way to kill a scanning program is to drown developers in findings they cannot act on. A source code scan that reports 400 issues, 380 of them irrelevant, trains everyone to close the tab.

A few tactics that work:

  • Baseline existing findings. When you first turn on SAST, snapshot the current issues and only fail on new ones. You will fix the backlog later.
  • Prioritize by reachability and severity. A critical CVE in a package you never call at runtime is lower priority than a medium one on your login path. Reachability analysis, where the tool checks whether vulnerable code is actually invoked, cuts noise sharply.
  • Suppress with a reason, not silently. When you dismiss a finding, record why in the tool so the next engineer does not re-litigate it.
  • Deduplicate across tools. If SAST and SCA both report the same underlying issue, merge them so it counts once.

An SCA tool such as Safeguard can flag a vulnerable dependency transitively and tell you whether the vulnerable function is reachable, which is the difference between a fix-now ticket and a note for later.

Fixing what you find

Scanning is worthless without remediation. Triage each confirmed finding into one of three buckets: fix now, fix on the next release, or accept the risk with a documented reason and expiry date.

For dependency findings, the fix is usually a version upgrade, sometimes a resolutions or overrides override to force a transitive dependency to a patched version. For code findings, it is a real change, so route those to the author of the code while the context is fresh. Track mean time to remediate as your program's health metric; the raw count of open findings matters far less than how quickly the dangerous ones close.

If you are choosing between commercial platforms, our comparison against Snyk breaks down how remediation workflows differ, and the Safeguard Academy has hands-on labs for tuning scan gates.

FAQ

What is the difference between SAST and SCA?

SAST analyzes the code your team wrote, looking for insecure patterns like injection or hardcoded secrets. SCA analyzes the open-source dependencies you pulled in, matching their versions against known vulnerability databases. You need both, because they cover different parts of your codebase.

How often should I scan source code for vulnerabilities?

On every pull request at minimum, plus a scheduled full scan of the main branch nightly or weekly. Per-PR scans catch new problems in the diff that introduced them; scheduled scans catch newly disclosed CVEs in dependencies you already shipped.

Can I scan code for vulnerabilities for free?

Yes. npm audit, Trivy, Semgrep, and Gitleaks are all free and cover SCA, SAST, and secret scanning respectively. Commercial tools add reachability analysis, richer remediation guidance, and central reporting, but you can build a credible baseline program with open-source tools alone.

Does a source code scan slow down my CI pipeline?

A well-scoped dependency scan adds seconds; full SAST can add minutes on large codebases. Keep pipelines fast by scanning only changed files on pull requests, caching dependency resolution, and running exhaustive scans on a schedule rather than blocking every commit.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.