## https://sploitus.com/exploit?id=0E40AC37-DCD2-59D5-8CB1-6FF9C21294B6
# Seal Security — JavaScript (npm) Example
A minimal, intentionally vulnerable Node.js/Express application used to demonstrate,
end‑to‑end, how [Seal Security](https://www.sealsecurity.io/) remediates a known CVE by
replacing a vulnerable dependency with a **sealed** (backported, drop‑in) version — with
**no change to your declared version ranges or your code**.
It is designed as a front‑to‑back smoke test for the Seal CLI in CI/CD: run the app, trigger
a real exploit, run Seal, and watch the same exploit get blocked.
---
## What this example demonstrates
| | |
|---|---|
| **Ecosystem** | JavaScript / npm |
| **Vulnerable package** | `ejs@2.7.0` (resolves to 2.7.4) |
| **CVE** | [CVE‑2022‑29078](https://nvd.nist.gov/vuln/detail/CVE-2022-29078) — EJS server‑side template injection → Remote Code Execution (CVSS 9.8) |
| **Sealed (fixed) version** | `ejs 2.7.4-sp1` from Seal's npm registry |
| **Integration** | Seal CLI as one build step — shown for both **GitHub Actions** and **Jenkins** |
The app also ships other well‑known vulnerable dependencies (`lodash 4.17.5`, `json5 0.5.1`,
`got 6.7.1`), each of which Seal also remediates to a sealed build.
---
## How the exploit works
The app spreads the entire URL query string straight into the EJS render call:
```js
const data = { name: 'World', ...req.query };
res.render('page', data, ...)
```
EJS accepts a `settings['view options']` object whose `outputFunctionName` value is written —
**unsanitized** — into the compiled template function body. An attacker can therefore inject
arbitrary JavaScript that runs on the server with the privileges of the Node.js process.
**Normal request**
```
/?name=alice
```
renders `Hello alice!`.
**Exploit request**
```
/?name=Hacker&settings[view%20options][outputFunctionName]=x;setTimeout(function()%7Bprocess.exit(1)%7D,3000);s
```
The server executes `setTimeout(function(){ process.exit(1) }, 3000)`. The page first loads and
clearly states that RCE succeeded; **reload a few seconds later** and you'll get
`ERR_CONNECTION_REFUSED` — the injected code killed the server, proving arbitrary code ran.
> The 3‑second delay is intentional: it lets the response reach the browser before the process
> exits, so you see the "exploit succeeded" page and then a clean crash rather than a frozen tab.
---
## Repository layout
```
.
├── index.js # the vulnerable Express app
├── views/ # EJS templates
├── package.json / package-lock.json
├── Jenkinsfile # example Jenkins (Groovy) pipeline with the Seal stage
└── .github/workflows/
├── build-and-run.yml # build + expose the app for browser testing
└── seal-security.yml # run Seal remediation, then start the app
```
---
## Prerequisites
Seal is **SaaS, Seal‑hosted** — nothing is installed inside your environment, and all traffic
is **outbound HTTPS on TCP 443** only. To run the remediation you need:
| Secret / credential | Used for | Where it goes |
|---|---|---|
| **Seal token** | Authenticating the Seal CLI | GitHub Actions secret `SEAL_TOKEN` / Jenkins "Secret text" credential `seal-token` |
| **ngrok token** *(optional)* | Exposing the running app to a browser for testing | GitHub Actions secret `NGROK_TOKEN` |
> Configure these in **Settings → Secrets and variables → Actions** (GitHub) or
> **Manage Jenkins → Credentials** (Jenkins). Never commit tokens to the repo.
Allowlist these Seal hosts for outbound 443:
`app.sealsecurity.io`, `authorization.sealsecurity.io`, `cli.sealsecurity.io`, and — for
sealed npm packages — **`npm.sealsecurity.io`**. The CLI binary is downloaded from
`github.com` / `objects.githubusercontent.com`.
---
## Run it locally
```bash
npm install
npm start # → http://localhost:3001
```
Open `http://localhost:3001/?name=alice` (works), then the exploit URL above (crashes the server).
---
## Remediate with Seal
The Seal CLI runs as **one extra step**, after `npm install` and before packaging. It scans the
resolved dependencies and rewrites the vulnerable ones to their sealed versions, using
**remote** fix mode (policy is managed centrally in the Seal UI).
### Option A — GitHub Actions
Uses [`seal-community/cli-action`](https://github.com/seal-community/cli-action):
```yaml
- uses: seal-community/cli-action@latest
with:
mode: fix
fix_mode: remote
token: ${{ secrets.SEAL_TOKEN }}
target: package-lock.json # the lock file for this ecosystem
```
Run it via **Actions → “Seal Security Remediation” → Run workflow**. See
[`.github/workflows/seal-security.yml`](.github/workflows/seal-security.yml).
### Option B — Jenkins (Groovy pipeline)
A single added stage, after install and before packaging. See [`Jenkinsfile`](Jenkinsfile):
```groovy
stage('Seal') {
steps {
sh '''
curl -fsSL https://github.com/seal-community/cli/releases/download/latest/seal-linux-amd64-latest -o seal
chmod +x seal
./seal fix --mode remote "$SEAL_MANIFEST" # SEAL_MANIFEST=package-lock.json
'''
}
}
```
`SEAL_TOKEN` comes from the `seal-token` Jenkins credential; set `SEAL_PROJECT` to your Seal
Project ID.
---
## What Seal changes
After `seal fix`, the vulnerable dependencies resolve to sealed builds from Seal's registry —
your `package.json` version *ranges* stay the same:
| Dependency | Before | After (sealed) |
|---|---|---|
| ejs | 2.7.4 | **2.7.4‑sp1** |
| lodash | 4.17.5 | **4.17.5‑sp1** |
| json5 | 0.5.1 | **0.5.1‑sp1** |
| got | 6.7.1 | **6.7.1‑sp1** |
A sealed version is the *same* package with the security fix backported, so it's a drop‑in
replacement — no code changes, no major‑version upgrade.
## Verify the fix
Re‑run the exploit URL against the remediated app. The injection no longer executes: the sealed
`ejs` rejects the malicious `outputFunctionName` and the app responds with **“Invalid parameter”**
instead of running the payload. The server stays up.
---
## How to add Seal to your own project
1. Add **one step** to your pipeline, **after** dependencies are installed and **before**
packaging/bundling.
2. Point `seal fix` at the specific manifest/lock file — `package-lock.json` for npm. For a repo
with multiple manifests, run one `seal fix` per manifest.
3. Use **remote** fix mode so your security team manages remediation policy centrally in the
Seal UI — nothing is committed to the repo.
4. Provide the Seal token via your CI secret store (GitHub secret / Jenkins credential).
That's the whole integration — one stage, outbound‑only, no changes to application code.