You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -271,6 +271,9 @@ All settings are overridable via environment variables prefixed with `PC2NUTS_`:
271
271
|`PC2NUTS_STARTUP_TIMEOUT`|`300`| Maximum seconds allowed for initial data loading. If exceeded, the service starts with whatever data was loaded and sets `data_stale: true`. |
272
272
|`PC2NUTS_DOCS_ENABLED`|`true`| Set to `false` to disable Swagger UI (`/docs`) and ReDoc (`/redoc`) in production. |
273
273
|`PC2NUTS_CORS_ORIGINS`|`*`| Comma-separated list of allowed CORS origins. Set to a specific origin (e.g. `https://example.com`) to restrict cross-origin access. Empty string disables CORS middleware. |
274
+
|`PC2NUTS_ACCESS_LOG_FILE`|*(empty — stdout)*| Path to access log file. When set, logs are written to this file with automatic rotation. When empty, access logs go to stderr. |
275
+
|`PC2NUTS_ACCESS_LOG_MAX_MB`|`10`| Maximum size of each access log file in MB before rotation. |
276
+
|`PC2NUTS_ACCESS_LOG_BACKUP_COUNT`|`5`| Number of rotated access log files to keep (e.g. 5 x 10 MB = 50 MB max disk usage). |
274
277
275
278
## Three-tier lookup
276
279
@@ -408,6 +411,7 @@ Changing the `PC2NUTS_EXTRA_SOURCES` list invalidates the SQLite cache automatic
408
411
-**HTTPS:** The service serves plain HTTP. Place it behind a TLS-terminating reverse proxy (nginx, cloud load balancer) in production.
409
412
-**Docker:** The container runs as a non-root user (`appuser`). The `/app/data` volume must be writable by this user. Pre-computed estimates are included in the image.
410
413
-**Rate limiting:** Limits are per-client IP (`X-Forwarded-For` aware). Behind a reverse proxy, ensure the proxy sets this header correctly.
414
+
-**Access logging:** Every request is logged with client IP, method, path, status code, and duration. Set `PC2NUTS_ACCESS_LOG_FILE` to write to a rotating file instead of stderr.
Copy file name to clipboardExpand all lines: docs/security_analysis.md
+19-15Lines changed: 19 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -78,9 +78,9 @@ An attacker with env var access (CI/CD, shared hosting, orchestration misconfigu
78
78
79
79
`url.rsplit("/", 1)[-1]` extracts only the last path component. Path traversal via URL crafting is not possible. ZIP contents are read into memory, never extracted to disk. No zip-slip risk.
80
80
81
-
### Disk full handling — LOW
81
+
### ~~Disk full handling~~ — RESOLVED (v0.6.1)
82
82
83
-
If the disk is full during `cached.write_bytes()`or SQLite operations, OSError propagates. For ZIPs, this fails silently per-country (other countries still load). For DB save, the `except Exception` handler cleans up the temp file. Adequate but not logged at ERROR level.
83
+
**Fixed:**`cached.write_bytes()`is now wrapped in try/except with `logger.error`. DB save exception upgraded from `logger.warning` to `logger.error`.
84
84
85
85
---
86
86
@@ -126,25 +126,25 @@ The app serves plain HTTP. Must be behind a TLS-terminating reverse proxy (nginx
126
126
127
127
**Fixed:**`requirements.lock` with exact pinned versions. Dockerfile uses `requirements.lock` for reproducible builds. `requirements.txt` retained with ranges for development.
No GitHub Actions, no automated tests, no linting, no security scanning in the repo.
131
+
**Fixed:** GitHub Actions CI workflow added with 4 jobs: lint (ruff), import check, security (pip-audit + bandit), and Docker build verification.
132
132
133
133
---
134
134
135
135
## 9. Configuration Robustness
136
136
137
-
### `nuts_version` "unknown" not handled — LOW
137
+
### ~~`nuts_version` "unknown" not handled~~ — RESOLVED (v0.6.1)
138
138
139
-
If `PC2NUTS_TERCET_BASE_URL` doesn't match `NUTS-\d{4}`, the version is "unknown". URL guessing generates invalid URLs (`NUTS-unknown`), discovery may still work, but the DB file is `postalcode2nuts_NUTS-unknown.db`. No validation or warning at startup.
139
+
**Fixed:** Startup now logs a warning when the NUTS version cannot be derived from the base URL.
140
140
141
-
### No validation on `db_cache_ttl_days` — LOW
141
+
### ~~No validation on `db_cache_ttl_days`~~ — RESOLVED (v0.6.1)
142
142
143
-
A value of 0 or negative causes the cache to always appear expired. No explicit bounds checking.
143
+
**Fixed:** Startup now logs a warning when `db_cache_ttl_days` is less than 1.
144
144
145
-
### JSON file corruption — LOW
145
+
### ~~JSON file corruption~~ — RESOLVED (v0.6.1)
146
146
147
-
If `settings.json` or `postal_patterns.json` are malformed, the app crashes at import time with a `json.JSONDecodeError`. This is fail-fast (good), but the error message won't be very user-friendly.
147
+
**Fixed:** Both JSON files now produce a clear `Fatal: failed to load <path>: <reason>` message and `SystemExit` instead of a raw traceback.
148
148
149
149
---
150
150
@@ -158,9 +158,9 @@ Once loaded, data is static. To refresh, restart the service. This is intentiona
158
158
159
159
If TERCET is unreachable on first startup and no SQLite cache exists, the app starts with zero data. The health endpoint reports `"no_data"` but the app is "up". All `/lookup` requests return 400. There's no automatic retry after startup.
160
160
161
-
### Estimates not revalidated after extra sources — LOW
161
+
### ~~Estimates not revalidated after extra sources~~ — RESOLVED (v0.6.1)
162
162
163
-
`_revalidate_estimates()`removes estimates that overlap with exact matches. But if extra sources overwrite a TERCET entry with a different NUTS3 code, existing estimates for that postal code are not updated to reflect the new mapping.
163
+
**Fixed:**`_revalidate_estimates()`now detects and warns when removed estimates had NUTS3 codes inconsistent with current exact data, flagging stale estimates for operator review.
164
164
165
165
---
166
166
@@ -183,7 +183,11 @@ If TERCET is unreachable on first startup and no SQLite cache exists, the app st
183
183
|~~**MEDIUM**~~|~~Mutable globals without locking~~| 6 |**RESOLVED v0.6.0**|
184
184
|**LOW**| Version exposed in OpenAPI spec | 4 | Open |
0 commit comments