Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
4d963e9
Prepare auto-ban response policy
dominikletica Jun 18, 2026
537b019
Document auto-ban scoring policy
dominikletica Jun 18, 2026
48ece22
Add auto-ban signal enforcement core
dominikletica Jun 18, 2026
601cb27
Add recovery login alias for auto-ban bypass
dominikletica Jun 18, 2026
d44be50
Expose owner auto-ban review workflows
dominikletica Jun 18, 2026
aa6262d
Document auto-ban review policy
dominikletica Jun 18, 2026
16b4d45
Keep recovery login on user route
dominikletica Jun 18, 2026
4e324c1
Document locale-aware delivery boundaries
dominikletica Jun 18, 2026
403e4f6
Harden auto-ban enforcement boundaries
dominikletica Jun 18, 2026
4a4344a
Allow auto-ban recovery login submissions
dominikletica Jun 18, 2026
c160314
Serialize auto-ban index updates
dominikletica Jun 18, 2026
306b779
Fail auto-ban reset on cache delete failure
dominikletica Jun 18, 2026
679da8b
Require auto-ban reset signal persistence
dominikletica Jun 18, 2026
663b5a5
Record trigger effects only for new auto-bans
dominikletica Jun 18, 2026
be97a5b
Keep newest auto-ban detail signals visible
dominikletica Jun 18, 2026
81610c9
Mark auto-ban API endpoints owner-only
dominikletica Jun 18, 2026
8f14543
Document auto-ban review hardening
dominikletica Jun 18, 2026
f791538
Verify auto-ban rollback clears active state
dominikletica Jun 18, 2026
ffe6291
Centralize ignorable request path skips
dominikletica Jun 18, 2026
78058ee
Require recovery marker for login ban bypass
dominikletica Jun 18, 2026
f1cca1e
Fail open auto-ban when config is unavailable
dominikletica Jun 18, 2026
e3378b8
Document second auto-ban review fixes
dominikletica Jun 18, 2026
2ec5daa
Enforce auto-bans over probe responses
dominikletica Jun 18, 2026
bb89778
Harden auto-ban inspection against malformed fields
dominikletica Jun 18, 2026
55522a2
Respect auto-ban responses during session binding
dominikletica Jun 18, 2026
5d24633
Use occurrence ids for auto-ban owner alerts
dominikletica Jun 18, 2026
4e56f1b
Document third auto-ban review fixes
dominikletica Jun 18, 2026
347bc06
Add suspicious payload security signals
dominikletica Jun 18, 2026
04e9be8
Show trigger geo context on auto-ban detail
dominikletica Jun 18, 2026
42e1c13
Document geo traffic-shedding follow-up
dominikletica Jun 18, 2026
36e6f47
Enforce auto-bans before probe and login side effects
dominikletica Jun 18, 2026
080942b
Release auto-bans before reset cutoffs
dominikletica Jun 18, 2026
fb9efbf
Serialize auto-ban reset cutoffs
dominikletica Jun 18, 2026
1846d3c
Preempt banned API and scheduler sources
dominikletica Jun 18, 2026
ec7a04e
Harden auto-ban audit follow-ups
dominikletica Jun 18, 2026
8e8a661
Update pull request template
dominikletica Jun 18, 2026
33a71f1
Count auto-ban floor by request
dominikletica Jun 18, 2026
0040fc1
Skip trusted scheduler source scoring
dominikletica Jun 18, 2026
cd11ee5
Validate auto-ban signal retention
dominikletica Jun 18, 2026
fc1cee9
Document auto-ban review fixes
dominikletica Jun 18, 2026
f372efe
Guard persisted signal retention bounds
dominikletica Jun 18, 2026
2859e32
Derive signal retention default from auto-ban TTL
dominikletica Jun 18, 2026
940b5e7
Bound additional persisted config values
dominikletica Jun 18, 2026
235f9dc
Recheck recovery login bans after auth
dominikletica Jun 18, 2026
ced55c5
Skip trusted scheduler payload scoring
dominikletica Jun 18, 2026
6aef450
Document auto-ban review fixes
dominikletica Jun 18, 2026
f096c2b
Bound trusted auto-ban access level
dominikletica Jun 18, 2026
7059c80
Recheck auto-bans after payload signals
dominikletica Jun 18, 2026
7d1f569
Scan JSON bodies for payload probes
dominikletica Jun 18, 2026
f2bfa76
Document auto-ban review updates
dominikletica Jun 18, 2026
e888d78
Suppress recovery auth failures after auto-ban
dominikletica Jun 18, 2026
0c5b3fc
Bound suspicious payload body scanning
dominikletica Jun 18, 2026
5d05c4a
Score anonymous application payload probes
dominikletica Jun 18, 2026
31ab2ae
Document auto-ban review hardening
dominikletica Jun 18, 2026
c8372f7
Block protected browser auto-ban bypasses
dominikletica Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [ ] Package/module boundaries, access levels, route/API/live endpoint scopes, and collision risks reviewed
- [ ] Setup/init/CI, cross-platform behavior, disabled-feature fallbacks, and process/env handling reviewed
- [ ] Project-rules-, architecture-, naming- and documentation-drift reviewed (see #57 for details)
- [ ] Codebase readability, naming, hierarchy, class map, frontend structure, and test-suite clarity reviewed (see #109 for details)
- [ ] Follow-up tasks captured in WORKLOG
- [ ] Updated / aligned translations and user-facing copy

Expand Down
14 changes: 14 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,20 @@ services:
$cachePool: '@cache.rate_limiter'
$lockFactory: '@lock.factory'

App\Security\AutoBan\AutoBanStore:
arguments:
$cache: '@cache.app'
$lockFactory: '@lock.factory'

App\Security\AutoBan\AutoBanSignalEvaluator:
arguments:
$environment: '%kernel.environment%'

App\Security\AutoBan\AutoBanRequestSubscriber:
arguments:
$environment: '%kernel.environment%'
$trustedApiKeys: '@App\Security\AutoBan\TrustedApiKeyAutoBanBypass'

App\Security\RateLimit\RateLimitRequestSubscriber:
arguments:
$environment: '%kernel.environment%'
Expand Down
21 changes: 11 additions & 10 deletions dev/CLASSMAP.md

Large diffs are not rendered by default.

88 changes: 34 additions & 54 deletions dev/WORKLOG.md

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion dev/WORKLOG_HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Developer Worklog History

> **Status**: Active
> **Updated**: 2026-06-17
> **Updated**: 2026-06-18
> **Owner**: Core
> **Purpose:** Preserve compacted branch/PR history moved out of `dev/WORKLOG.md` at branch boundaries.

## Usage
Move completed branch or PR logs from `dev/WORKLOG.md` into this file when switching branches or after a PR is merged. Keep the active worklog focused on the current branch so reviewers can see the full PR context while older project history stays available.

## Archived Branches
### 2026-06-17 to 2026-06-18 feat-security-rate-enforcement
- Implemented the rate-enforcement slice: descriptor-backed Symfony RateLimiter facade, Owner-gated mode setting (`off`, `standard`, `strict`, `panic`), action-cost-derived policy catalogue, Website/API/Scheduler/Auth/Setup/Probe buckets, fail-open storage diagnostics through the Message layer, authenticated multipliers, Owner ordinary exemption, recovery-login handling, active-profile scoped resets, dormant captcha reset contract, and redacted HTML/JSON `429`/probe responses.
- Hardened the branch through 39 resolved Cloud Review findings: unsafe-only workflow charging, authentication-failure ordering, generated/static exclusions, active-profile resets, recovery buckets, account/token subjects, API/CORS/read-only preflight handling, Owner and scheduler exceptions, exact technical path scopes, probe ordering before package/API/setup/maintenance gates, setup-final-apply safety, multi-bucket pre-check/commit semantics, website fallback for descriptor gaps, and segment-bound API/Cron guards.
- Added shared path helpers, HTTP error-renderer bare/resolve behavior, `render:route` diagnostics hardening, rate-limit Security settings translations, future cache-panic documentation, worklog/class-map/draft updates, PR-readiness/review-fix project rules, and final review notes showing full `bin/phpunit`, `bin/jstest`, and `bin/lint` passed before merge.

### 2026-06-16 to 2026-06-17 feat-security-admin-acl-enforcement
- Implemented the Admin ACL enforcement slice: domain-owned feature registry, denied/visible/mutable states, surface inference from feature keys, seeded Owner-configurable defaults, ACL-group override states, Owner-gated `Settings/ACL` matrix, dynamic active-package settings rows, and feature-matrix caching with explicit invalidation.
- Wired Admin ACL feature checks through protected settings fields, Admin navigation/views, package/theme actions, package lifecycle and settings, GeoIP maintenance, operations continuations, scheduler, logs, statistics, users, user reviews, ACL group management, backup/status surfaces, and Admin API handlers while keeping visible-only controls rendered disabled where layout depends on them.
Expand Down
37 changes: 19 additions & 18 deletions dev/draft/0.2.x-SecurityHardeningPlan.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Security hardening implementation plan

> **Status**: Draft
> **Updated**: 2026-06-15
> **Updated**: 2026-06-18
> **Owner**: Core
> **Purpose:** Plan the security hardening feature split so each branch can be implemented, reviewed, and merged as a focused production-ready slice.

Expand Down Expand Up @@ -35,7 +35,7 @@ The old Grav plugin `sec-lookup` at `/Volumes/Projekte/temp/sec-lookup` may be u
- Recognize cross-action abuse. Separate buckets remain useful, but repeated activity across different guarded workflows should also feed a global subject budget and suspicious-signal store.
- Add progressive punishment as a first-class concept: observe, throttle, require captcha, temporarily block, and hard-block only when signals justify it.
- Support active punishment such as draining a suspicious subject's relevant buckets when clear bot behavior is detected.
- Add temporary auto-bans with TTL for IP, visitor ID, API key, or combined subjects. Authenticated users should receive softer handling where reasonable, and Owner accounts must never be locked out of all recovery paths.
- Add score-based temporary auto-bans with TTL for Visitor-ID and stable client-IP subjects. Trusted registered users are never auto-banned, and Owner accounts must never be locked out of all recovery paths.
- Treat GeoIP as operational metadata for logs, statistics, and security review. Missing provider configuration must degrade gracefully.
- Include IconCaptcha in the overall security feature cut, but keep its provider implementation in a dedicated branch after the generic captcha contract.
- Cover adjacent security surfaces through the abuse/rate policy catalogue instead of local ad hoc checks: setup apply, CORS preflight, high-impact admin operations, package lifecycle, backup/restore, import/export, uploads/archives, diagnostic downloads, and support bundles.
Expand Down Expand Up @@ -174,11 +174,11 @@ Detailed plan: [auto-ban](security-hardening/auto-ban.md).

Scope:

- Add TTL-based ban records for IP, visitor ID, API key, and combined subjects.
- Add ban reasons, expiry, source signals, actor context, and audit entries.
- Apply softer thresholds or bypasses for authenticated users where appropriate.
- Enforce Owner safety so at least one active Owner retains a documented recovery path.
- Provide Admin review and manual unban tools.
- Score retained source-risk Security signals across a one-hour window, primarily by Visitor ID and secondarily by stable client-IP bucket/HMAC with a laxer threshold multiplier.
- Record low-weight Security signals for `400`, `403`, `404`, and `429` outcomes, correlating probe-plus-`400` evidence so one request is not double-counted and excluding login-required `401` by status alone.
- Store active TTL ban state through cache-flock-backed keys plus a cache-backed active-ban index, with escalation derived from retained ban-trigger `security_signal_event` records.
- Add required Config/Settings defaults for auto-ban enablement, trusted-user minimum access level, and score threshold through the settings/default provider so missing database states fail open.
- Add forced bare `403` ban responses, trusted-user/Owner/API-key recovery protection, active-ban review UI, detail pages backed by filtered Security signals, and Owner-gated manual reset.

Non-goals:

Expand All @@ -187,8 +187,8 @@ Non-goals:

Acceptance:

- Clear bot/probe behavior can be temporarily blocked without blocking all Owner recovery.
- Operators can understand why a subject is blocked and when the block expires.
- Clear repeated suspicious behavior can be temporarily blocked without blocking trusted users or Owner recovery.
- Operators can understand why a subject is blocked from retained Security signals, see expiry, and reset the active ban immediately.

### `feat-security-captcha-contract`

Expand Down Expand Up @@ -292,7 +292,7 @@ Acceptance:
- Security identity must come from one reviewed resolver that uses Symfony's resolved request client IP. Security code must not trust raw `X-Forwarded-*` headers, ad-hoc IP parsing, package-owned client identity logic, or app-level trusted-proxy settings introduced by Security branches; trusted proxy handling belongs in deployment/webserver configuration. Visitor-ID generation may use raw forwarding-header values only as untrusted differentiation entropy and never as Security subject, GeoIP, ban, or signal evidence.
- TTL, expiry, and cleanup behavior must use an injectable clock/time boundary so tests can cover expiry, replay, and cleanup deterministically.
- Every enforcement branch must define its degraded-storage behavior explicitly. Optional observability features may fail open with redacted diagnostics; hard enforcement must avoid surprise Owner lockout and must audit degraded decisions.
- Race and idempotency behavior must be reviewed for one-shot captcha validation, limiter consumption/reset, auto-ban creation/manual unban, mail token delivery, and remember-me token rotation.
- Race and idempotency behavior must be reviewed for one-shot captcha validation, limiter consumption/reset, auto-ban creation/manual reset, mail token delivery, and remember-me token rotation.
- Each PR must complete the Security PR-readiness checklist below from the actual branch diff. Do not pre-check items from the template without reviewing the changed public entry points, data flows, browser storage, package boundaries, docs, translations, and verification output for that branch.

## Security PR-readiness checklist
Expand All @@ -310,21 +310,22 @@ Acceptance:

## Fixed implementation defaults

- Auto-ban storage uses database-backed TTL records plus cleanup. Cache may be added later as a speed layer, but the first reviewable implementation must keep Admin review and audit persistence understandable.
- Auto-ban enforcement applies by default to anonymous/IP/visitor/API probe abuse. Authenticated users start with softer handling such as throttling or captcha, and Owner accounts must retain recovery access.
- Auto-ban is enabled by default once implemented, but can be disabled through bounded Security settings. Visitor IDs and IP buckets tied to active Admin or Owner sessions must not be banned.
- IP-based enforcement is secondary, laxer than Visitor-ID enforcement, and short-lived. Prefer Visitor-ID-backed TTL bans for continuity, add IP TTL bans only to reduce cookie-reset bypasses, and keep every IP ban TTL below 30 days.
- Auto-ban active state uses cache-flock TTL keys. Admin review, escalation, and reset cutoffs are explained by retained `security_signal_event` records, including ban-trigger and reset signals, rather than a separate durable ban table.
- Auto-ban enforcement applies by default to Visitor-ID and IP source evidence. Trusted registered users at or above the configured trusted-user level, including valid API keys owned by trusted users, are never auto-banned, and Owner accounts must retain recovery access.
- Auto-ban is enabled by default once implemented, but can be disabled through bounded Security settings. Visitor IDs and IP buckets tied to trusted active sessions or trusted-user-owned API keys must not be banned.
- IP-based enforcement is secondary and laxer than Visitor-ID enforcement through a fixed `x2` threshold multiplier. Prefer Visitor-ID-backed scoring for continuity, evaluate IP scoring to reduce cookie/header-mutation bypasses, and keep every active ban TTL at or below 7 days.
- Scoreable request signals are persisted per evaluated source subject, normally Visitor ID and IP bucket, so scoring uses indexed subject reads rather than JSON-context filtering. Score aggregation is write-triggered after signal persistence and reuses that DB path; ordinary non-signal requests perform only the cheap active-ban cache check. If both Visitor and IP thresholds cross from one evaluation, create at most one active ban and prefer the Visitor ban.
- Passive suspicious signals use database-backed short-lived records with redacted normalized subject keys, intent, reason code, weight/count, first/last seen timestamps, expiry, and safe context hash. They are not enforcement by themselves until the rate/ban branches consume them.
- Security subject keys use normalized client identity, visitor ID, API key fingerprint/prefix, authenticated user UID, and safe combined keys produced by the shared resolver. Raw IP strings and raw credentials must not become cross-branch storage keys.
- Raw IP addresses, IP buckets, and stable IP-derived hashes are queryable for at most 30 days across logs, projections, diagnostics, exports, and backups. Longer-term correlation uses visitor IDs, authenticated user IDs, API key fingerprints, or aggregate dimensions.
- Turbo and browser prefetch are classified server-side and count at lower confidence. Disable prefetch only on expensive or side-effect-adjacent links.
- Website global rate policy uses separate deliberate burst and sustained buckets so normal browsing is not measured by one oversized per-minute limit. Turbo/browser prefetch uses a separate lower-confidence observation path instead of spending the same budget as deliberate navigation.
- Registered users receive higher ordinary navigation/API limits than anonymous visitors where the workflow has no explicit bucket. Owner-owned API keys and subjects tied to active Owner sessions are exempt from ordinary application rate-limit rejection.
- Suspicious probe paths are configurable with extensive defaults, return a generic `400`, and allow only one high-signal probe per subject per 10 minutes before draining suspicious buckets or feeding auto-ban decisions.
- A recovery login path such as `/user/login?bypass=1` must remain reachable even when the current Visitor ID or IP bucket is banned; it uses a dedicated small recovery bucket and successful credential login re-evaluates current bans and limiters under authenticated, Admin, or Owner policy.
- Suspicious probe paths are configurable with extensive defaults, return a generic `400`, and allow only one high-signal probe per subject per 10 minutes before draining suspicious buckets or feeding auto-ban decisions. Auto-ban must correlate the probe signal and its `400` response so one request is not double-counted.
- The `/user/login?bypass=1` recovery login render path, resolved through the shared `RequestPathResolver`, must remain reachable even when the current Visitor ID or IP bucket is banned; it uses a dedicated small recovery bucket and successful credential login re-evaluates current bans and limiters under authenticated, trusted-user, Admin, or Owner policy.
- Successful login and verified provider-backed captcha are the first scoped reset candidates. Registration and password reset remain stricter until a branch explicitly proves a safe reset policy.
- Captcha-based reset or `429` recovery requires an active provider-backed challenge. Provider `none`, missing-provider, or disabled-provider auto-success is never human proof and must not reset limits.
- The first Admin ban-review UI should be a compact diagnostics/review surface with active bans, expired cleanup, reason/source signal, expiry, actor context where available, and manual unban.
- The first Admin ban-review UI should be a compact diagnostics/review surface with active bans, expired cleanup, reason/source signal, expiry, actor context where available, and Owner-gated manual reset.
- IconCaptcha challenge state uses a dedicated Symfony cache pool when practical, falling back to `cache.app` if no dedicated pool exists. The first provider default TTL is 15 minutes, with one-shot invalidation after every validation attempt and scoped failure buckets preventing brute-force guessing.
- IconCaptcha accessibility must not reveal the visual answer through labels. If neutral labels are insufficient, use a provider-owned accessible quiz challenge with a spoken question/task and multiple answer options, generated and validated through the same one-shot challenge, TTL, and abuse-signal rules.
- Account mail delivery uses provider-backed flow metadata, localized Markdown templates, Messenger queueing, and an initial transport guard of one queued message per account-flow action plus configurable worker-side retry/backoff. Debug action-link logging remains disabled outside explicit debug mode.
Expand All @@ -333,7 +334,7 @@ Acceptance:
## Remaining calibration points

- Define exact first thresholds while implementing `feat-security-abuse-foundation` and `feat-security-rate-enforcement`; record them as constants/config defaults and tests in those branches.
- Decide whether a future cache acceleration layer is needed after database-backed auto-ban behavior is measured.
- Verify the early auto-ban enforcement ordering carefully: active Visitor/IP bans must be resolved before error pages or rate-limit buckets can produce another response, but after authenticated trusted-user and trusted-user-owned API-key context is available to bypass those source bans.
- Database-backed lookup projections for message, audit, access, and passive security signals are the preferred Admin/API read model for Security review and abuse correlation. File logs remain the durable raw source and operator fallback.
- Define backup/export handling for short-retention security data before enabling any database-backed security projection, so restore and support workflows do not reintroduce expired IP-derived records.

Expand Down
Loading