- POST /api/auth/login: 10 attempts per minute per TCP peer IP
(X-Forwarded-For ignored), then 429 with Retry-After. The block starts
at 1 minute and doubles on each attempt while blocked and on each new
block, capped at 15 minutes. A successful login clears the IP. No
per-account lockout. In-memory, idle entries swept after 15 minutes,
at most 10,000 IPs.
- New passwords (init, change-password, /setup page) need 12 characters.
Login does not check length, so existing 8-character passwords work.
- The binary's -addr default is 127.0.0.1:8787. The Docker image passes
-addr :8787 explicitly and is unchanged.
- GET /api/health returns {"ok":true} without a valid token; with one it
still returns service and version for the web UI top bar.
The server wrote jwt.key to the executable's directory, /app in the
image, so it lived in the container's writable layer and every container
rebuild generated a new key and signed all users out.
- ARTEX_JWT_KEY_DIR selects the key directory (default: config.BaseDir(),
unchanged for non-Docker installs). docker-compose.yml sets it to
/app/keys and bind-mounts ./keys there; the Dockerfile declares the
volume. The key stays out of data/, which the file manager browses.
- loadOrCreateJWTKey creates the directory (0700). When keyDir/jwt.key is
missing it migrates dataDir/jwt.key, then BaseDir()/jwt.key (mode 0600,
old file removed), otherwise generates a new key. An existing key is
never overwritten; a short one is now a startup error.
- .gitignore and .dockerignore exclude keys/.
- README and CHANGELOG updated; the 0.5.0 known issue is removed.
POST /api/auth/init accepted any caller while auth.password_hash was empty,
so whoever reached port 8787 first could set the admin password and take the
instance. When no password is set at startup, the server now generates a
random one-time setup token and writes it to the server log. authInit
compares it in constant time, rejects a missing or wrong token with 403, and
clears it once the password is stored.
The setup page has a field for the token and api.initPassword sends it.
docker-compose publishes 8787 on 127.0.0.1 only, like 8788. README says where
to find the token.
Comments, doc comments, UI labels, log and error messages, LLM prompt
strings, markdown docs, test data and assertions. Identifiers, enum
keys, routes, placeholders and code structure are unchanged.
Notes:
- docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md
- tests that exercised CJK specific handling (GBK zip names, Chinese
full text search) now use ASCII equivalents; go build passes and the
test suite matches the pre translation baseline (remaining failures
need a PostgreSQL DSN and fail identically on the original)
- frontend mention syntax now uses English kinds (finding, asset, ...)
matching the backend, chat mentions tests pass