Commit Graph

17 Commits

Author SHA1 Message Date
5297e6033a fix: abort startup on explicitly empty db_url instead of deriving
The derived file:...state.sqlite3 URL is a default and defaults apply
only to omitted keys: db_url set to an empty string now aborts naming
the key, matching the existing behavior of state_dir: "".
2026-08-07 17:03:12 +00:00
c0d325156f fix: abort startup when a config key is explicitly set to null
An explicitly-null key (port: null, bare port:, debug: ~, metrics
subkeys, and every other known key) previously fell through the ok/nil
check in the strict getters and silently took the default, violating
the no-silent-fallback rule and contradicting metrics: null which
already aborted. validateKnownKeys now collects null-valued keys (top
level and metrics subkeys) and aborts naming each one, and the strict
getters and validateAllowlistHostsValue error on null instead of
defaulting as defense in depth. This also replaces the unhelpful
'value <nil> is not a map of metrics settings' rendering for
metrics: null with the null-specific message.
2026-08-07 17:02:16 +00:00
11e9206c21 fix: suppress gosec G703 false positive on state_dir probe removal
The pinned CI linter (golangci-lint v2.10.1) flags os.Remove(probePath)
in ensureStateDirWritable as G703 path traversal via taint analysis.
probePath comes from os.CreateTemp inside the StateDir that the probe
just validated, so the taint finding is a false positive; suppress it
with a justified nolint comment matching the existing precedent in
loadConfigFile. Verified against the pinned linter version via the
Dockerfile lint stage (0 issues).
2026-08-07 17:00:20 +00:00
2fb0801dc6 feat: validate configuration on startup, fail fast on bad config (closes #52)
Some checks failed
check / check (push) Failing after 42s
A config value that is set but unparseable or invalid now aborts
startup with an error naming the offending key and value; defaults
apply only to omitted keys. Unknown top-level config keys and unknown
metrics subkeys abort startup naming each unknown key, so typos like
whitelist_hosts fail immediately instead of being silently ignored. A
config file that exists at a standard location but fails to parse is
now a fatal error instead of being skipped with a warning. state_dir
is verified creatable and writable with a probe file before the
listener binds. Port must be in 1-65535 (fractional values are
rejected, not truncated), upstream_connections_per_host must be at
least 1, allowlist_hosts entries must be bare hostnames, sentry_dsn
must be a valid URL when set, and metrics credentials must be set
together. The stale signing_key comment in config.example.yml (keyless
mode was never implemented) now states the actual requirement.
TODO.md records the completed step per its Workflow section.
2026-08-07 16:37:30 +00:00
f19da2c02c test: add failing startup config validation tests (#52)
Encode the required fail-fast behavior as tests ahead of the
implementation: a config value that is SET but unparseable or invalid
must abort startup (defaults apply only to OMITTED keys), unknown
top-level keys and unknown metrics subkeys must abort naming the key,
a malformed config file at a standard location must abort instead of
being skipped with a warning, and state_dir must be creatable and
writable at startup. Mechanically extracts newFromSmartConfig from
config.New so the construction path is testable without fx; current
lenient behavior is unchanged, so the new enforcement tests fail.
2026-08-07 16:31:03 +00:00
6573b9d1ef refactor: extract signature package from imgcache (#46)
All checks were successful
check / check (push) Successful in 5s
Extracts HMAC-SHA256 request signing out of `internal/imgcache/` into its own `internal/signature/` package, per the plan in [issue #39](#39).

This is one of the remaining "easily separable" extractions (`imageprocessor`, `allowlist`, `magic`, and `httpfetcher` already landed). Only the signer is moved here so the diff stays reviewable.

## What moved

From `internal/imgcache/signature.go` and its tests into `internal/signature/`:

- `Signer` type, its `New` constructor, `Sign`, `Verify`, `GenerateSignedURL`
- `ParseParams` (query-string signature/expiration parsing)
- Signature error sentinels

## One-way import edge

To keep the import edge one-way (`imgcache` depends on `signature`, never the reverse), the package defines a standalone `Request` type carrying just the fields the signature covers, instead of importing `imgcache.ImageRequest`. `imgcache` projects its `ImageRequest` onto `signature.Request` via a small unexported `signatureRequest` helper. This mirrors how the `magic` extraction defined its own `ImageFormat` type.

## Renames (no stuttering)

- `NewSigner` -> `signature.New`
- `ParseSignatureParams` -> `signature.ParseParams`
- `ErrSignatureRequired`/`Invalid`/`Expired` -> `signature.ErrRequired`/`Invalid`/`Expired`

The `ErrRequired` message is updated from "non-whitelisted host" to "non-allowlisted host" for inclusive terminology, consistent with the `allowlist` rename.

## Rework (post-review)

Three commits added after review feedback:

- `d69019b` — golden known-answer test pinning the exact HMAC signatures and signed URL paths for three fixed vectors (resized, resized+query, orig size), cross-validated against an independent HMAC implementation. Any change to the signed byte format now fails loudly.
- `43b9f1c` — whitelist→allowlist rename completed across `internal/imgcache` and `internal/handlers` (`ServiceConfig.Allowlist`, `Allowlist` interface, `IsAllowlisted`, test helpers and test names).
- `3dc1999` — one-pass config surface rename, no back-compat alias: YAML key `whitelist_hosts` → `allowlist_hosts`, `Config.WhitelistHosts` → `Config.AllowlistHosts`, `config.example.yml`, `scripts/manual-test.sh`, and `README.md` (which also documented a nonexistent `source_host_whitelist` key — now fixed to the real one).

## Behavior

Pure refactor apart from the config key rename above. The bytes fed to the HMAC are unchanged (`host:path:query:width:height:format:expiration`), so previously issued signatures remain valid — now enforced by the golden test. All existing tests move with the package. `script/cibuild` passes at head `3dc1999` (fmt-check, lint, test, build).

refs #39

Co-authored-by: sneak <sneak@sneak.berlin>
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #46
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-08-07 17:44:00 +02:00
clawbot
85729d9181 fix: update Dockerfile to Go 1.25.4 and resolve gosec lint findings
All checks were successful
check / check (push) Successful in 1m41s
- Update Dockerfile base image from golang:1.24-alpine to golang:1.25.4-alpine
  (pinned by sha256 digest) to match go.mod requirement of go >= 1.25.4
- Fix gosec G703 (path traversal) false positives by adding filepath.Clean()
  at call sites with nolint annotations for internally-constructed paths
- Fix gosec G704 (SSRF) false positive with nolint annotation; URL is already
  validated by validateURL() which checks scheme, resolves DNS, and blocks
  private IPs
- All make check passes clean (lint + tests)
2026-02-25 05:44:49 -08:00
02dedd433b Require signing_key at startup, add default config
- Add config validation: signing_key required, minimum 32 characters
- Server now fails to start without valid signing_key (no more runtime errors)
- Add config.example.yml with default whitelist hosts
- Copy config to /etc/pixa/config.yml in Docker image
- Update entrypoint to use --config /etc/pixa/config.yml
- Add config.dev.yml for local Docker development
- Mount dev config in make devserver
2026-01-08 15:48:37 -08:00
d2e2e319be Create /var/lib/pixa directory in Docker image for database/cache
- Change default StateDir from ./data to /var/lib/pixa (proper Unix convention)
- Create directory owned by pixad user in Dockerfile
- Set WORKDIR to /var/lib/pixa
2026-01-08 15:37:25 -08:00
f244d9c7e0 Add per-host connection limits for upstream fetching
- Add upstream_connections_per_host config option (default: 20)
- Implement per-host semaphores to limit concurrent connections
- Semaphore released when response body is closed
- Prevents overwhelming origin servers with parallel requests
2026-01-08 05:19:20 -08:00
6a20406b0f Add -config flag using cobra to specify config file path 2026-01-08 04:58:05 -08:00
9647829ac9 Support YAML list format for whitelist_hosts config 2026-01-08 04:08:51 -08:00
fd2d108f9c Wire up image handler endpoint with service orchestration
- Add image proxy config options (signing_key, whitelist_hosts, allow_http)
- Create Service to orchestrate cache, fetcher, and processor
- Initialize image service in handlers OnStart hook
- Implement HandleImage with URL parsing, signature validation, cache
- Implement HandleRobotsTxt for search engine prevention
- Parse query params for signature, quality, and fit mode
2026-01-08 04:01:53 -08:00
29adb6ee47 Rename unused parameters to _ to satisfy linter 2026-01-08 02:27:45 -08:00
d95d31df25 Rename ConfigParams to Params to avoid stuttering 2026-01-08 02:24:20 -08:00
ce25ea8f6f Define DefaultPort and DefaultStateDir constants 2026-01-08 02:21:00 -08:00
516853626d Add basic webserver skeleton with healthcheck 2026-01-08 02:20:23 -08:00