feat: HOST validated at the adapter boundary and controls the actual bind interface (E00-S04-T04)
The environment adapter now resolves HOST through resolveHost, validating it at the adapter boundary as a hostname (RFC 1123) or IP address (IPv4/IPv6, node:net isIP); an invalid HOST throws a field-specific ConfigStartupError naming host, so arbitrary env content is never used for binding or echoed verbatim into the startup log (issue acceptance criterion, resolving security review finding SEC-3). The server passes config.host to server.listen(config.port, config.host, ...), so a configured HOST binds exactly that interface and the startup log never claims a bind the process does not enforce (resolving SEC-2).
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "EPPP public server application. Serves the application health endpoint (E00-S02-T03) gated on the startup migration run (E00-S03-T06), with a field-specific startup error when a required setting is missing (E00-S04-T02), automatic secret redaction from all log output (E00-S04-T03) and all settings flowing through the config package's environment adapter — the server reads no process.env directly (E00-S04-T04); the Fastify 5 application shell lands in a later story.",
|
||||
"description": "EPPP public server application. Serves the application health endpoint (E00-S02-T03) gated on the startup migration run (E00-S03-T06), with a field-specific startup error when a required setting is missing (E00-S04-T02), automatic secret redaction from all log output (E00-S04-T03) and all settings flowing through the config package's environment adapter — the server reads no process.env directly and binds the validated HOST interface (E00-S04-T04); the Fastify 5 application shell lands in a later story.",
|
||||
"scripts": {
|
||||
"build": "pnpm --filter @personal-blog/config build && pnpm --filter @personal-blog/database-postgres build && tsc -p tsconfig.json",
|
||||
"typecheck": "pnpm --filter @personal-blog/config build && pnpm --filter @personal-blog/database-postgres build && tsc -p tsconfig.json --noEmit",
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
* (`HOST`/`PORT`/`DATABASE_URL`/`EPPP_SESSION_SECRET`) onto the validated
|
||||
* config shape and validates it with `assertValidConfig` (E00-S04-T02) before
|
||||
* the server binds, so a missing required setting is still a startup error
|
||||
* naming the missing field.
|
||||
* naming the missing field. `HOST` is validated at the adapter boundary as a
|
||||
* hostname or IP address and the server passes `config.host` to
|
||||
* `server.listen`, so a configured `HOST` binds exactly that interface and
|
||||
* the startup log reflects the actual bind — it never claims a bind the
|
||||
* process does not enforce, and never echoes unvalidated env content.
|
||||
*
|
||||
* [E00-S04-T03] secret redaction: ALL log output goes through the redacting
|
||||
* logger (`createLogger`, defined below — every line is scrubbed of the
|
||||
@@ -199,7 +203,11 @@ if (databaseUrl === undefined) {
|
||||
});
|
||||
}
|
||||
|
||||
server.listen(config.port, () => {
|
||||
// The server binds the validated bind interface: `config.host` (default
|
||||
// `0.0.0.0`, validated as a hostname/IP by the adapter) is passed to
|
||||
// `server.listen`, so a configured `HOST` binds exactly that interface and
|
||||
// the startup log reflects the actual bind.
|
||||
server.listen(config.port, config.host, () => {
|
||||
logger.log(`@personal-blog/server listening on http://${config.host}:${config.port} (health: GET /health)`);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user