Skip to main content

SAST

SAST (Static Application Security Testing) scans source code or compiled binaries for security vulnerabilities without executing the program. Integrated into CI pipelines, tools like Semgrep, SonarQube, and CodeQL catch injection flaws, secrets, and insecure patterns before code reaches production.

Understanding SAST

What Is SAST in Simple Terms

SAST is a security reviewer that reads your code automatically. Instead of waiting for a penetration test or a security audit, SAST runs on every pull request and flags potential vulnerabilities before the code is merged. SQL injection risks, hardcoded passwords, insecure cryptography, path traversal bugs -- SAST catches these at the earliest and cheapest point in the development lifecycle.

At Razorpay, every pull request is scanned by Semgrep before it can be merged. Engineers see security feedback alongside test results -- in the same PR, at the same time.

How It Works

◈ DIAGRAM
+------------------------------------------+
| Source code (PR or commit) |
+------------------------------------------+
|
v
+------------------------------------------+
| SAST Tool parses code |
| Builds AST (Abstract Syntax Tree) |
| No code execution needed |
+------------------------------------------+
|
v
+------------------------------------------+
| Matches patterns against rule sets |
| Rule: user input reaches SQL query? |
| Rule: private key in string literal? |
| Rule: MD5 used for password hashing? |
+------------------------------------------+
|
v
+------------------------------------------+
| Findings with severity: INFO/WARN/ERROR |
| File, line number, explanation |
| Pass/fail based on severity threshold |
+------------------------------------------+

SAST in GitHub Actions with Semgrep:

YAML
sast-scan:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Run Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
## Pre-built rulesets for common vulnerability classes
config: >
p/nodejs
p/secrets
p/sql-injection
p/xss
p/owasp-top-ten
## Generate SARIF for GitHub Security tab
generateSarif: true
- name: Upload SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep.sarif
## CodeQL for deeper analysis (GitHub native)
codeql:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3

Semgrep custom rules:

YAML
## .semgrep/custom-rules.yaml
## Block patterns specific to your codebase
rules:
## Catch direct database queries without parameterisation
- id: unsafe-query-construction
pattern: |
db.query("..." + $INPUT)
message: |
Direct string concatenation in SQL query.
Use parameterised queries: db.query(sql, [params])
severity: ERROR
languages: [javascript]
## Catch console.log of potentially sensitive objects
- id: log-sensitive-data
pattern: console.log($REQ.body)
message: Logging request body may expose passwords or tokens.
severity: WARNING
languages: [javascript]

Practical Commands

Bash
## Install Semgrep
pip install semgrep
## Run locally before committing
semgrep --config=p/nodejs src/
semgrep --config=p/secrets .
## Run with specific rule file
semgrep --config=.semgrep/custom-rules.yaml src/
## Output JSON for processing
semgrep --config=p/owasp-top-ten --json src/ | \
jq '.results[] | {file: .path, line: .start.line, message: .extra.message}'
## SonarQube scan (if using SonarQube)
sonar-scanner \
-Dsonar.projectKey=payment-api \
-Dsonar.sources=src/ \
-Dsonar.host.url=https://sonar.internal \
-Dsonar.token=$SONAR_TOKEN

Troubleshooting

Symptom Check What to Look For
Too many false positives Review rules used Disable overly broad rules
SAST blocking on old code Baseline file Semgrep supports baseline to ignore existing findings
Scan too slow Rule count Use targeted rulesets, not p/all
Tip

Start SAST with p/secrets ruleset only. Secret detection has very low false positive rates and catches real critical issues -- API keys, passwords, private keys accidentally committed. Once the team is comfortable with SAST blocking pipelines, gradually add broader rulesets like p/owasp-top-ten.

Remember

SAST finds potential vulnerabilities in code -- it does not prove they are exploitable. A SQL injection finding still needs to be reviewed to confirm the input actually reaches the database without sanitisation. SAST is a signal, not a verdict. Triage findings by severity and context.

Security

Configure SAST to block only on HIGH and CRITICAL findings. Blocking on MEDIUM and LOW creates noise that causes teams to ignore the scanner entirely. A blocked pipeline with genuine HIGH findings gets fixed. A pipeline blocked every day on INFO findings gets continue-on-error: true added and never gets fixed.

Common Mistake

Running SAST as a reporting-only step that never blocks the pipeline. If SAST findings do not block merges, engineers stop reading them within a week. SAST must be a gate -- HIGH and CRITICAL findings block the PR. This is the only way to ensure findings get fixed rather than accumulated.

Frequently Asked Questions

What kinds of vulnerabilities does SAST catch that a code reviewer typically misses?

SAST tools trace data flow through the actual call graph — following a variable from an untrusted input source through the code to a dangerous sink like a SQL query or shell command — which is tedious and error-prone for a human to verify by eye across a large codebase. It reliably catches classes like SQL injection, hardcoded credentials, path traversal, and insecure deserialization, because these follow detectable patterns even when the vulnerable code path spans multiple files or functions.

Why do teams often end up ignoring SAST findings entirely after a while?

Default SAST rule sets tend to generate a high volume of false positives and low-severity noise, and if a pipeline gate blocks merges on every finding without triage, engineers quickly learn to treat the tool as an obstacle rather than a signal — and start reflexively dismissing alerts, including real ones. The fix is tuning the rule set to the codebase, gating only on high-confidence/high-severity findings, and routing lower-severity results to a dashboard for periodic review instead of blocking every PR.