Skip to content

Instantly share code, notes, and snippets.

@kekru
Last active September 19, 2025 11:11
Show Gist options
  • Save kekru/ec02d97b26b891cea0c44b3d65d2b1aa to your computer and use it in GitHub Desktop.
Save kekru/ec02d97b26b891cea0c44b3d65d2b1aa to your computer and use it in GitHub Desktop.
Scan minified node.js JavaScript apps like Angular with your docker container scanner

Scan minified node.js JavaScript apps like Angular with your docker container scanner

In the (docker) container world, we should use cve scanners like Trivy (free) or JFrog Xray to scan for known vulnerabilities.
In most cases, this just works.
But it typically does not work for statically compiled and minified JavaScript apps like Angular or React apps.

This is how to make your minified frontend app scannable with common scanners.

In my tests during september 2025, this worked for:

It did not work for

How do container vulnerability scanners work?

Vulnerability scanners typically do the following

  • Download a database of known vulnerabilities.
    In my tests JFrog Xray often knows much more vulnerabilities, than Trivy. JFrog seems to have quite a good database
  • Trying to find out, which components are inside an image and creating an SBOM on the fly.
    They look for linux package managers, common named files, descriptor files like a package-lock.json, etc. to find out what is installed
  • Compare the created SBOM with the database of known vulnerabilities

What is the problem with our minified frontend apps?

Our frontend apps are compiled and minified to a folder of static html and javascript files.
These files are put into a webserver like nginx.

We loose the information, what the sources are, from that the app was built.
Vulnerability scanners can not find out anymore, which node_modules were needed to build the app, so they can not create a valid SBOM.

How to solve the problem?

We need to help the vulnerability scanner by creating an SBOM at build time and provide it to the final docker image.
We use cdxgen here.
I also tested trivy to create an SBOM, but cdxgen created a way larger SBOM with much more identified packages.

If you want to try this out yourself, clone this gist:
git clone https://gist.github.com/ec02d97b26b891cea0c44b3d65d2b1aa.git

Create the SBOM

Lets say we have a small project with the provided package.json with a few dependencies.

Add the cdxgen to the package.json with a run script

{
  ...
  "scripts": {
    "build-app": "echo 'Hello World' > index.html", // Here you would build your app
    "create-sbom": "cdxgen --type npm --output sbom.cdx.json", // Create the sbom. File name is important
    "pretty-sbom": "node -e \"const fs=require('fs');fs.writeFileSync('sbom.cdx.json',JSON.stringify(JSON.parse(fs.readFileSync('sbom.cdx.json','utf8')),null,2))\""
  },
  ...
  "devDependencies": {
    "@cyclonedx/cdxgen": "11.5.0"
  }
}

Install and create the sbom

npm install # or "npm ci" to be more strict
npm run build-app # run whatever you need for your app to build

# Create the sbom and prettify the resulting json
npm run create-sbom
npm run pretty-sbom

The file ending *.cdx.json is important, at least for Trivy, see SBOM Detection inside Targets

Add SBOM to your container image

The final step is just copy the sbom.cdx.json into the root of your container image.
The vulnerability scanners will find it there.

Lets build a docker image based on nginx, with our app and the SBOM added:

docker-compose.yaml:

services:
  app:
    image: example.com/my-app:0.0.1
    pull_policy: build
    build:
      context: .
      dockerfile_inline: |
        FROM nginx:1.28.0-alpine
        # index.html is your app
        COPY index.html /usr/share/nginx/html/index.html
        # SBOM containing all modules that were needed to build the app
        COPY sbom.cdx.json /sbom.cdx.json
    ports:
      - 80:80
# Build it, resulting in an image "example.com/my-app:0.0.1"
$ docker compose build

# Scan using trivy -> it should find vulnerabilities for axios and form-data
$ trivy image  --severity HIGH,CRITICAL example.com/my-app:0.0.1
Node.js (node-pkg)
==================
Total: 2 (HIGH: 1, CRITICAL: 1)

┌───────────┬────────────────┬──────────┬────────┬───────────────────┬─────────────────────┬────────────────────────────────────────────────┐
│  Library  │ Vulnerability  │ Severity │ Status │ Installed Version │    Fixed Version    │                     Title                      │
├───────────┼────────────────┼──────────┼────────┼───────────────────┼─────────────────────┼────────────────────────────────────────────────┤
│ axios     │ CVE-2025-58754 │ HIGH     │ fixed  │ 1.8.4             │ 1.12.0              │ axios: Axios DoS via lack of data size check   │
│           │                │          │        │                   │                     │ https://avd.aquasec.com/nvd/cve-2025-58754     │
├───────────┼────────────────┼──────────┤        ├───────────────────┼─────────────────────┼────────────────────────────────────────────────┤
│ form-data │ CVE-2025-7783  │ CRITICAL │        │ 4.0.2             │ 2.5.4, 3.0.4, 4.0.4 │ form-data: Unsafe random function in form-data │
│           │                │          │        │                   │                     │ https://avd.aquasec.com/nvd/cve-2025-7783      │
└───────────┴────────────────┴──────────┴────────┴───────────────────┴─────────────────────┴────────────────────────────────────────────────┘


# Scan using docker scout (only if you have a paid Docker Desktop)
$ docker scout cves --only-severity critical,high example.com/my-app:0.0.1
# In a paid Docker Desktop, you can also open the Docker Desktop UI -> Images -> Search "example.com/my-app:0.0.1" and click "Start analysis"

# If your "example.com" is your Artifactory with Xray enabled, push the app and view the results in Artifactorys web ui
$ docker compose build --push

Summary

Whenever you have a compiled/minified app, that can not be easily introspected by common scanners, provide an SBOM in the cyclonedx format and put it into /sbom.cdx.json inside your resulting container image.

Then the vulnerability scanners should find it and use it as the source to compare against their vulnerability database.

node_modules/
sbom.cdx.json
index.html
# this is only for demo purposes, in your real project you want to check in the package-lock.json
package-lock.json
services:
app:
image: example.com/my-app:0.0.1
pull_policy: build
build:
context: .
dockerfile_inline: |
FROM nginx:1.28.0-alpine
# index.html is your app
COPY index.html /usr/share/nginx/html/index.html
# SBOM containing all modules that were needed to build the app
COPY sbom.cdx.json /sbom.cdx.json
ports:
- 80:80
{
"name": "scan-minified-node-apps",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"build-app": "echo 'Hello World' > index.html",
"create-sbom": "cdxgen --type npm --output sbom.cdx.json",
"pretty-sbom": "node -e \"const fs=require('fs');fs.writeFileSync('sbom.cdx.json',JSON.stringify(JSON.parse(fs.readFileSync('sbom.cdx.json','utf8')),null,2))\""
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/ec02d97b26b891cea0c44b3d65d2b1aa.git"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://gist.github.com/ec02d97b26b891cea0c44b3d65d2b1aa"
},
"homepage": "https://gist.github.com/ec02d97b26b891cea0c44b3d65d2b1aa",
"dependencies": {
"axios": "1.8.4",
"form-data": "4.0.2"
},
"devDependencies": {
"@cyclonedx/cdxgen": "11.5.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment