We take the security of the CLI seriously because it handles a credential (your ZPL API key) that can be used to spend money and burn quota on your account. This document explains how to report vulnerabilities, what's in scope, and what defences ship by default.
Do NOT open a public GitHub issue for security reports. Instead:
- Email:
security@zeropointlogic.io(preferred) orcontact@zeropointlogic.io. - Subject: start with
[SECURITY]so it gets triaged on the same day. - Include: the CLI version (
zpl --version), OS, a minimal reproducer, and what you believe the impact is.
We commit to:
- Acknowledge within 2 business days.
- Initial assessment within 5 business days.
- Patch + coordinated release within 30 days for high-severity issues (key exfiltration, RCE, auth bypass), 90 days for medium (DoS, secret leak in logs), best effort for low.
- Credit you in
CHANGELOG.mdand the GitHub release notes once the patch ships, unless you ask to remain anonymous.
If you do not receive an acknowledgement within 5 business days, please
re-send to security@zeropointlogic.io.
In scope:
- The published
zpl-engine-clinpm package and its source on github.com/cicicalex/zpl-engine-cli. - Any CLI behaviour that handles, stores, or transmits the API key.
- Local file handling (config, history, backup) on supported platforms (macOS, Linux, Windows).
- Anything that could route an authenticated request to an unintended host.
Out of scope:
- Vulnerabilities in third-party dependencies — please report those upstream first; we'll fast-track the advisory once patched.
- The ZPL engine itself — report engine-side issues to
security@zeropointlogic.ioseparately. - Self-hosted or modified copies — only the npm-published binary is covered.
- Social-engineering attacks against ZPL employees.
The CLI ships these protections out of the box. Most of them have unit
tests in test/ and you can verify their behaviour from zpl about:
- API key is never written to logs / errors. All four secret-shape
patterns (
zpl_u_*,zpl_s_*,Bearer …,sk-…,gsk_…) are redacted in:~/.zpl/history.json(statusfield —db.tssanitiseStatus)dieFormattedstderr output (index.tssanitiseErrorMessage)zpl diagnoseAPI key field (always shown aszpl_u_cli_***prefix)
- Config file mode 0600 on POSIX.
requireConfigwarns yellow if it finds anything more open and prints the exactchmod 600fix. - Backup file mode 0600 on POSIX.
zpl repair's pre-deletion backup is chmoded immediately after copy. ZPL_API_KEYenv var validated. Format-checked with the same regex aszpl loginissues; service keys (zpl_s_*) are rejected with a redirect to the dashboard.
- Engine URL host allowlist.
engine.base_urlfrom any source must behttps://AND match*.zeropointlogic.io(or a host added viaZPL_ENGINE_HOST_ALLOWLIST). Hostile URLs are rejected before any Bearer-tokenised request leaves the box. - HTTPS only.
http://engine URLs are rejected — no plain-text Bearer tokens. - No userinfo in URLs.
https://user:pass@host/...is rejected (those credentials end up in proxy logs and referer headers). - Cloudflare HTML detection. Any 200-or-4xx response with HTML
body is surfaced as
ApiCloudflareErrorwith the cf-ray ID, preventing JSON-parse crashes that would expose the request. - Proxy support honours NO_PROXY. Sensitive hosts can be excluded from a corporate TLS-inspecting proxy.
- File reads capped at 1 MB.
zpl check/compare/diffrefuse to read enormous files that would OOM the process. - No TOCTOU race. Single
stat-then-readper file instead ofexistsSync → readFileSync.
- No forced exit while fetch is in flight. Avoids the libuv assertion on Windows that pre-v1.0 could leak partial output.
- Recursive exit-code enforcement. Unknown command / option exits
1 instead of 0 (POSIX-compliant for
set -escripts).
The CLI sends one outbound request to npm's registry on startup,
solely to detect new versions. No telemetry, no usage analytics, no
phone-home. Disable with ZPL_SKIP_UPDATE_CHECK=1.
The engine logs requests for billing purposes only. Logs never feed training data.
If you want to verify the npm tarball hasn't been tampered with:
npm view zpl-engine-cli@latest dist.shasumCompare with the SHA-1 you compute locally:
shasum -a 1 $(npm pack zpl-engine-cli@latest --dry-run --json | jq -r '.[0].filename')(Future improvements: package signing via npm provenance once we wire
SLSA into the CI release flow. Tracked in CHANGELOG.)
The CLI relies on Node 18+'s native fetch / undici stack for TLS. CA
certificates come from the system trust store (override with
NODE_EXTRA_CA_CERTS=/path/to/ca.pem for corporate MITM proxies).
Minimum TLS 1.2 enforced by Node defaults; we do not ship a
NODE_TLS_REJECT_UNAUTHORIZED=0 escape hatch.
Thank you to the security researchers who have responsibly disclosed issues. Credits land here as patches ship.