feat(anonymisation-bases-passthrough): implement spec (#155)#188
feat(anonymisation-bases-passthrough): implement spec (#155)#188rubenvdlinde wants to merge 7 commits into
Conversation
- AnonymizationController and BatchAnonymizationController now validate
optional per-entity bases[] (must be array of strings; HTTP 400 on
malformed input); additive, backward-compatible.
- EntityDetectionService::mapEntitiesForAnonymization forwards bases
verbatim when present; empty [] forwarded as []; absent field omitted.
- AnonymizationService::extractAndDetectEntities attaches prohibitionMatch
per entity via PolicyMatchService (null when service not yet installed).
- prohibitionMatch shape: null or {ruleId, ruleName, highConfidence};
highConfidence = confidence >= threshold (>=0.85 default, configurable
via docudesk.prohibition.high_confidence_threshold IAppConfig key).
- Tests: EntityDetectionServiceTest (bases forwarding), AnonymizationService
ProhibitionTest (6 prohibition-match scenarios), AnonymizationController
Test (bases validation), BatchAnonymizationControllerBasesTest (batch
bases validation). NC interface stubs added for standalone test runs.
- Newman collection: 3 new anonymization integration test cases.
- docs/features/anonymization.md: documents bases passthrough, prohibition
match field, and CHANGELOG entries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
isset() already eliminates null from the type; the subsequent === null comparison was unreachable and flagged by PHPStan as a strict-comparison error on mixed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-18 19:30 UTC
Download the full PDF report from the workflow artifacts.
Code Review — Juan Claude van DammeResult: PASS (6 applied fixes, 0 unfixed, 0 blocking) FixesGate-7 (no-admin-idor) — both controllers, 13 methods All Gate-14 (route-reachability) — appinfo/routes.php The 9 PHPCS — AnonymizationController.php:111
AdvisoriesMCP coverage (advisory) — This app does not yet publish an Out-of-scope inherited debt (informational)Phpmd flagged several violations in files NOT in this PR's diff: Checks
🤖 Changes Juan Claude van Damme applied
View full diff · 11 files changed, 222 insertions(+), 13 deletions(-) |
| * @NoAdminRequired | ||
| * @NoCSRFRequired | ||
| */ | ||
| public function files(): JSONResponse |
There was a problem hiding this comment.
[fixed: gate-7 / ADR-005] Added IUserSession guard (Http::STATUS_UNAUTHORIZED) — all 4 @NoAdminRequired methods (files, upload, extract, anonymize). Mirrors SigningController::verify() precedent.
| $statusCode | ||
| ); | ||
| } | ||
| }//end try |
There was a problem hiding this comment.
[fixed: PHPCS] phpcbf added missing //end try comment.
| * @NoAdminRequired | ||
| * @NoCSRFRequired | ||
| */ | ||
| public function batchUpload(): JSONResponse |
There was a problem hiding this comment.
[fixed: gate-7 / ADR-005] Added IUserSession guard to all 9 @NoAdminRequired methods (batchUpload, folderBatch, batchExtract, batchStatus, batchEntities, batchAnonymize, batchReport, getProfiles, updateProfiles). updateProfiles was a false-positive at the old line 366 (NoAdminRequired from preceding getProfiles docblock fell within the 20-line lookback); after line-number shift from the constructor param addition it's no longer flagged, but guard added defensively.
| ['name' => 'batch_anonymization#batchEntities', 'url' => 'api/anonymization/batch/{batchId}/entities', 'verb' => 'GET'], | ||
| ['name' => 'batch_anonymization#batchAnonymize', 'url' => 'api/anonymization/batch/{batchId}/anonymize', 'verb' => 'POST'], | ||
| ['name' => 'batch_anonymization#batchReport', 'url' => 'api/anonymization/batch/{batchId}/report', 'verb' => 'GET'], | ||
| ['name' => 'batchAnonymization#folderBatch', 'url' => 'api/anonymization/batch/folder', 'verb' => 'POST'], |
There was a problem hiding this comment.
[fixed: gate-14 / ADR-029] Renamed batch_anonymization# → batchAnonymization# (9 entries). Gate-14 derives slug batchAnonymization from BatchAnonymizationController.php and does a fixed-string match; snake_case and camelCase are both valid in Nextcloud but the gate's match requires camelCase.
| $this->mockL10n = $this->createMock(IL10N::class); | ||
| $this->mockL10n->method('t')->willReturnCallback(fn($s) => $s); | ||
|
|
||
| $mockUser = $this->createMock(IUser::class); |
There was a problem hiding this comment.
[fixed: test-consistency] Added IUserSession mock returning non-null IUser so existing 400/200 assertions still reach the bases-validation logic after the new user null-check guard.
| parent::setUp(); | ||
|
|
||
| $this->mockRequest = $this->createMock(IRequest::class); | ||
| $this->mockAnonService = $this->createMock(BatchAnonymizeService::class); |
There was a problem hiding this comment.
[fixed: test-consistency] Same IUserSession mock setup as AnonymizationControllerTest.
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ✅ | ✅ 529/529 | |||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/10 statements)
Quality workflow — 2026-05-19 03:13 UTC
Download the full PDF report from the workflow artifacts.
481f223 to
b7431b3
Compare
…onymisation-bases-passthrough
b7431b3 to
f8e7a8f
Compare
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ⏭️ | ||||
| phpcs | ⏭️ | ||||
| phpmd | ⏭️ | ||||
| psalm | ⏭️ | ||||
| phpstan | ⏭️ | ||||
| phpmetrics | ⏭️ | ||||
| eslint | ⏭️ | ||||
| stylelint | ⏭️ | ||||
| composer | ⏭️ | ⏭️ | |||
| npm | ⏭️ | ⏭️ | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ |
Quality workflow — 2026-05-19 03:20 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ✅ | ✅ 529/529 | |||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/10 statements)
Quality workflow — 2026-05-19 03:24 UTC
Download the full PDF report from the workflow artifacts.
Security Review — Clyde BarcodeResult: PASS (5 fixed, 0 unfixed blocking, informational findings noted) Fixes
Security Checks
Informational (non-blocking)
See inline comments for per-finding detail. 🤖 Changes Clyde Barcode applied
View full diff · 4 files changed, 125 insertions(+), 3 deletions(-) |
|
📍 [fixed: merge-conflict] Resolved conflict between PR version 0.0.34-unstable.8 and origin/development 0.0.33; kept PR version. |
|
📍 [fixed: gate-7 auth guard] Rule: hydra-gate-7 / OWASP A01:2021 — added IUserSession injection and requireAuthenticated() guard to files(), upload(), extract(), anonymize(). Matches SigningController sibling pattern. |
|
📍 [fixed: gate-7 auth guard] Rule: hydra-gate-7 / OWASP A01:2021 — added IUserSession injection and requireAuthenticated() guard to all 8 @NoAdminRequired methods. Matches SigningController sibling pattern. |
| @@ -179,6 +194,10 @@ public function upload(): JSONResponse | |||
| public function extract(int $fileId): JSONResponse | |||
| { | |||
There was a problem hiding this comment.
[informational: pre-existing] Rule: OWASP A01:2021 — extract(fileId) passes URL-supplied file ID to OpenRegister without controller-level per-object ownership check. Auth guard (requireAuthenticated) verifies authentication only. Per-object scoping may be handled by OpenRegister's service layer. Recommend follow-up audit of TextExtractionService file access.
| @@ -198,6 +217,10 @@ public function batchExtract(string $batchId): JSONResponse | |||
| */ | |||
| public function batchStatus(string $batchId): JSONResponse | |||
There was a problem hiding this comment.
[informational: pre-existing] Rule: OWASP A01:2021 — batchStatus(batchId) and sibling batch methods accept URL-supplied batchId without controller-level ownership check. BatchStateService may enforce per-user scoping. Recommend verifying.
| * Set up test environment | ||
| * | ||
| * @return void | ||
| */ |
There was a problem hiding this comment.
[fixed: test-fix] Added IUserSession mock returning non-null user to setUp() to satisfy new requireAuthenticated() guard in controller.
| * Set up test environment | ||
| * | ||
| * @return void | ||
| */ |
There was a problem hiding this comment.
[fixed: test-fix] Added IUserSession mock returning non-null user to setUp() to satisfy new requireAuthenticated() guard in controller.
…erTest Controller now takes 13 ctor args; the FolderTest still passed 12.
…lderTest Controller now returns 401 when userSession->getUser() is null; the bare IUserSession mock returns null by default.
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ✅ | ✅ 529/529 | |||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/10 statements)
Quality workflow — 2026-05-19 04:03 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ✅ | ✅ 529/529 | |||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/10 statements)
Quality workflow — 2026-05-19 04:12 UTC
Download the full PDF report from the workflow artifacts.
Closes #155
Summary
Auto-generated draft PR for OpenSpec change
anonymisation-bases-passthrough.The Hydra builder ran the spec but could not run
gh pr createitself(Phase D+E credential strip — Claude has no
GH_TOKENby design).The entrypoint detected commits on the feature branch with no PR and
created this draft so the reviewer + security + applier can proceed.
Spec Reference
/spec//spec/proposal.md/spec/design.md/spec/tasks.mdCommits on this branch
Files changed
appinfo/routes.phpdocs/features/anonymization.mdlib/Controller/AnonymizationController.phplib/Controller/BatchAnonymizationController.phplib/Service/AnonymizationService.phplib/Service/EntityDetectionService.phpopenspec/changes/anonymisation-bases-passthrough/tasks.mdtests/newman/docudesk-api.postman_collection.jsontests/stubs/OpenRegisterStubs.phptests/unit/Controller/AnonymizationControllerTest.phptests/unit/Controller/BatchAnonymizationControllerBasesTest.phptests/unit/Service/AnonymizationServiceProhibitionTest.phptests/unit/Service/EntityDetectionServiceTest.phpPR auto-created by Hydra builder entrypoint (
hydra_ensure_pr_exists)because Claude's session closed without running
gh pr create.Reviewer + applier follow as normal.