Skip to content

Scanning your code ​

People miss things, so let a program check too. A security scanner reads your project and points at secrets, known-vulnerable libraries and risky code before anyone else finds them. This lesson explains what scanners look for, how to read what they report, and how to run DL Sentinel on your machine and in CI.

Three things worth scanning for ​

WhatThe questionExample
SecretsIs a password, key or token written in a file?AWS_SECRET_ACCESS_KEY=… in a committed .env
Vulnerable dependenciesDoes the project use a library version with a known security hole?An old version of a logging library listed in package-lock.json
Insecure codeDoes the code use a pattern that is often exploitable?SQL built by joining strings with user input

Lesson 2 covered why secrets matter. The other two need a few new words.

Dependencies and CVEs ​

Most projects are mostly other people's code. A dependency is a library your project uses, listed in a manifest such as package.json, requirements.txt, go.mod or Cargo.toml. A lockfile (package-lock.json, Cargo.lock) records the exact versions installed.

When a security hole is found in a library and made public, it usually gets a CVE id: an entry in the public Common Vulnerabilities and Exposures list, in the form CVE-year-number. For example, CVE-2021-44228, known as Log4Shell, is the id of a serious flaw in the Java logging library Log4j. A CVE entry says which versions are affected and which are fixed.

A dependency scanner compares your versions with a database of such advisories. The fix is almost always the same: upgrade to a fixed version, and update the lockfile.

A related word: a CWE is not a specific bug but a kind of weakness, such as CWE-798 "use of hard-coded credentials" or CWE-89 "SQL injection". Scanners often tag findings with CWE ids and with the OWASP Top 10, a widely used list of the most common web application risks.

Insecure code and static analysis ​

Static analysis means reading code without running it, looking for risky patterns. Here is one that every scanner knows:

js
// Risky: the name goes straight into the SQL text
function findUserByName(db, name) {
  const query = "SELECT * FROM users WHERE name = '" + name + "'";
  return db.query(query);
}

If name comes from a user, they can type ' OR '1'='1 and change what the query does. That is SQL injection. The safe version passes the value separately, as a parameter, so it can never become part of the SQL:

js
// Safe: a parameterized query
function findUserByName(db, name) {
  return db.query('SELECT * FROM users WHERE name = $1', [name]);
}

DL Sentinel's source scanner reports the first version as a high-severity finding and stays quiet on the second. Other patterns it looks for include shell commands built from strings, eval of user input, MD5 used for passwords, and TLS certificate checks turned off.

False positives and false negatives ​

A scanner matches patterns. It does not understand your program, so it gets some things wrong in both directions:

What happenedExample
False positiveIt reports a problem that is not realA fake key in a test file, flagged as a secret
False negativeIt misses a real problemA secret split across two lines, or a SQL query built in a way the rule does not recognise

So treat every finding as a lead to check, not a proof, and a clean report as "nothing obvious found", not "secure". DL Sentinel helps you judge with two separate ratings on each finding:

  • Severity: how bad it would be if real (critical, high, medium, low, info).
  • Confidence: how sure the scanner is (high, medium, low).

Run DL Sentinel on your project ​

DL Sentinel runs offline on Linux, needs Node.js 20 or later, and never sends your code anywhere. Install it from source with the install guide, which sets up the sentinel command. Then, in a project you own:

bash
sentinel scan .

Here is the summary for the deliberately insecure sample project that comes with DL Sentinel:

text
DL Sentinel v0.1.0

Scanning: /home/you/dl-sentinel/tests/fixtures/vulnerable-app

secrets           7 findings
dependencies      5 findings
source            11 findings
infra             18 findings
api               8 findings
smart-contract    7 findings

Critical: 11
High:     28
Medium:   6
Low:      4
Info:     7

Scan completed.

The summary shows counts only. For every finding, write a full report:

bash
sentinel scan . --format markdown --output ~/my-app-report.md

Each finding has a rule id (the first letter names the scanner: S secrets, D dependencies, C source, I infrastructure, A API, SC smart contract), a file and line, evidence, and a recommendation. Secret values in evidence are always replaced with [REDACTED]. A finding in a JSON report looks like this:

json
{
  "id": "a3b939d090f6905d",
  "ruleId": "C003",
  "title": "SQL query built from a dynamic string",
  "severity": "high",
  "confidence": "medium",
  "file": "src/unsafe.js",
  "line": 23,
  "evidence": "return db.query(query);",
  "recommendation": "Use parameterized queries or prepared statements …"
}

Exit codes ​

Every scan ends with an exit code, which is how scripts and CI know the result:

CodeMeaning
0Scan finished and the policy passed
1Findings at a failing severity were found (by default critical and high)
2The scan or the tool hit an error
64The command was used wrongly, for example an unknown option

Dependency checks need data ​

Out of the box, DL Sentinel does not look up vulnerable versions. Its dependencies scanner reports D002, "check skipped", at severity info, instead of guessing. To check against the public OSV vulnerability database, add this to a sentinel.yml in the project:

yaml
scanners:
  dependencies:
    dataSource: osv

That setting sends the names and versions of your dependencies to the OSV service, never your source code. See The scanners.

Decide what to do with each finding ​

  1. Check it. Open the file at the line given and decide whether it is real.

  2. Fix real problems. Follow the recommendation. For a committed secret, rotate it first, as in lesson 2; deleting it from the file is not enough.

  3. Suppress reviewed false alarms. Add a suppress entry to sentinel.yml with a reason and an expires date, so it comes back for review. See Configure scans.

  4. Baseline old findings you cannot fix yet. See below.

Baselines: adopt a scanner on an old project ​

The first scan of an older project can report dozens of findings. Failing every build until all are fixed is not realistic. A baseline records today's findings, so later scans report and fail only on new ones:

bash
sentinel baseline .                                    # writes .sentinel-baseline.json
sentinel scan . --baseline .sentinel-baseline.json     # reports only new findings

The second command adds a line such as New: 0 (Baselined: 54) to the summary, and only new findings can make it exit 1. Commit the baseline file so your team and CI share it. It holds finding ids, not code or secret values.

UseWhen
A baselineYou have a backlog of old findings and want to stop new ones while you work through it
A suppress entryYou have reviewed a finding and decided it is acceptable

A baseline hides problems from the report; it does not fix them. Plan to work through it, critical and high first. See Baselines.

Scan in CI ​

CI (continuous integration) runs checks automatically on every push or pull request. Because a scan's exit code is 1 when it finds failing severities, the CI job fails and the problem is caught before it is merged.

On GitHub, DL Sentinel has a ready-made Action that reports only what each pull request adds, comments on the pull request and writes a SARIF file for GitHub code scanning. The central step of the workflow is:

yaml
- uses: Dhiva-Labs/dl-sentinel/apps/action@v0.1.0
  with:
    path: .
    base_path: .sentinel-base
    fail_on: critical,high

The full workflow, with the checkout steps and SARIF upload, is in Use DL Sentinel in CI. In any other CI system, build DL Sentinel from source and run the command:

bash
node apps/cli/dist/main.js scan "$PROJECT_DIR" \
  --format sarif --output sentinel.sarif \
  --fail-on critical,high --quiet

Add --baseline .sentinel-baseline.json to fail only on new findings. The CI automation guide explains automated checks in general.

Stay safe ​

  • Scan only code you own or may test. DL Sentinel reads files and attacks nothing, but its reports describe weaknesses. Scanning someone else's code and publishing the results can harm them.
  • Treat reports as sensitive. Keep report.md and other reports out of version control, and think before publishing one.
  • A clean scan is not a guarantee. Use it alongside code review, updates and the habits from earlier lessons.
  • Load plugins only if you have read them. A DL Sentinel plugin is code that runs with your permissions.

Check yourself ​

QuizScanning your code

Question 1 of 6

What is a CVE?

Score: 0

What you learned ​

  • Scanners look for secrets, vulnerable dependencies (with CVE ids) and insecure code patterns (static analysis).
  • Every finding is a lead to check: severity says how bad, confidence says how sure.
  • sentinel scan . scans, --format markdown --output writes the details, and the exit code gates CI.
  • A baseline lets you stop new problems on an old project while you fix the backlog.

Next steps ​