Security¶
Reporting a vulnerability¶
Please report vulnerabilities privately through GitHub private vulnerability reporting rather than in a public issue. The supported versions and the disclosure process are described in the security policy.
Server¶
- Input validation:
id(max 50 characters) andkeyword(max 100) must be single strings; control characters and< > " 'are stripped, and requests left without a usable parameter are rejected with HTTP 400 before any cache or Amazon lookup. - Rate limiting:
/productis limited per client IP (Server.rateLimitper minute) to protect your Amazon quota from abuse. - Client IP: always taken from Express
req.ip, governed byServer.trustProxy; rawX-Forwarded-Foris never trusted directly. KeeptrustProxyto the exact number of proxies (or their addresses):truewould let clients spoof their IP and bypass the rate limit. A trusted hop must also be the only way in: the Docker default (1) is only safe when the port is reachable by the local reverse proxy alone, which is why the Compose stack and thedocker runexamples publish it on127.0.0.1. Without a proxy, setSERVER_TRUST_PROXY=false. - Error handling: unexpected errors return a generic message unless
Server.debugistrue; never enable debug in production. - Headers:
X-Powered-Byis disabled. CORS is off by default (Server.cors). - Credentials: Amazon and Redis secrets are passed as
environment variables (for example from a
.envfile that is never committed) or kept inconfig/production.yml; the committed configuration files only hold empty values, and neither the npm package nor the Docker image contains credentials. - Administration portal: disabled by default (
Admin.enabled). Password of 12 characters at least, HMAC-signed tokens, failed logins rate limited per IP, strictContent-Security-Policyandno-storeon every response. Publish it to admins only if you can, see Administration portal. - AI insights: disabled by default (
Admin.insights). When enabled, the aggregated statistics of the analysed period (counters, product titles, page URLs without query string) are sent to the Claude API; no IP address, user agent or cookie is stored or sent. See AI insights. - Swagger UI: disabled by default (
Server.swagger). When enabled, it is served from the AMW origin with a strictContent-Security-Policy, a fixed OpenAPI URL and no external validator (see HTTP API).
Widget¶
The widget runs on third-party pages, so every value coming from the API is treated as untrusted:
- text is written with
textContent, never with rawinnerHTML; - image and product URLs go through
safeUrl(), which only acceptshttp:andhttps:(nojavascript:ordata:URLs); - the only HTML built from product data (price with a savings badge) is escaped with
sanitizeText(); - JSON-LD is serialized with
JSON.stringifyinto a<script type="application/ld+json">element throughtextContent; - product links use
target="_blank"withrel="sponsored nofollow".
Docker image¶
- runs as the non-root
amwuser (UID 1001); application files are owned by root and read-only for that user, and nothing is written to disk at runtime (logs go to stdout); docker/docker-compose.ymlruns it with a read-only root filesystem, every Linux capability dropped,no-new-privilegesand memory/CPU limits; the CI smoke test starts the image with the same restrictions;- the
node:26-alpinebase image is pinned by digest (updated by Dependabot); - production dependencies only, built in a separate stage;
npm,npx,corepackandyarnare removed from the runtime image;dumb-initas PID 1 for correct signal handling.- in the compose stack, Redis requires a password (
REDIS_PASSWORD), so that another container of the network cannot rewrite the cached products.
Supply chain¶
npm audit --audit-level highruns in CI and blocks merges.- Trivy scans the built Docker image (Alpine packages and the Node.js runtime) in CI and fails on high or critical vulnerabilities that have a fix.
- The dependency review workflow blocks pull requests that add dependencies with known high or critical vulnerabilities or an incompatible copyleft license.
- GitHub CodeQL code scanning (default setup) is enabled, and Dependabot keeps npm packages, GitHub Actions, Docker images and Compose files up to date.
- Workflows use a read-only default
GITHUB_TOKEN; write permissions are granted per job only where needed (packages, releases, Pages).