Skip to content

Commit d90863c

Browse files
committed
feat(redaction): 🛡️ Implement PDF redaction service and API endpoints
Introduce a secure PDF redaction service (`pdf-lib`) and API endpoints for: - Applying permanent, irreversible redactions to PDF documents (MP-6 compliance). - Generating previews of redaction areas. - Retrieving PDF document metadata (page count/dimensions). - Redacting PII patterns from log text (AU-3 compliance). Refactor logging middleware: - Centralize logger configuration using Effect Schema for validation. - Expand PII redaction paths for comprehensive log sanitization. Other changes: - Rename Astro components for consistency (e.g., `AgenciesPage.tsx` -> `agencies-page.tsx`). - Update encryption/decryption utilities to be asynchronous. - Add missing copyright headers to several files.
1 parent 7d97282 commit d90863c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+4388
-706
lines changed

apps/api/drizzle/0001_careless_tyrannus.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
-- Copyright (c) 2025 Foia Stream
2+
--
3+
-- Permission is hereby granted, free of charge, to any person obtaining a copy
4+
-- of this software and associated documentation files (the "Software"), to deal
5+
-- in the Software without restriction, including without limitation the rights
6+
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
-- copies of the Software, and to permit persons to whom the Software is
8+
-- furnished to do so, subject to the following conditions:
9+
--
10+
-- The above copyright notice and this permission notice shall be included in all
11+
-- copies or substantial portions of the Software.
12+
--
13+
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
-- SOFTWARE.
20+
121
CREATE TABLE `api_keys` (
222
`id` text PRIMARY KEY NOT NULL,
323
`user_id` text NOT NULL,

apps/api/drizzle/meta/_journal.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@
2424
"breakpoints": true
2525
}
2626
]
27-
}
27+
}

apps/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"hono-pino": "^0.10.3",
3434
"jose": "^6.1.3",
3535
"nanoid": "^5.1.6",
36+
"pdf-lib": "^1.17.1",
3637
"pino": "^10.1.0",
3738
"pino-pretty": "^13.1.3",
3839
"stoker": "^2.0.1",

apps/api/src/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { env } from './config/env';
4343
import configureOpenAPI from './lib/configure-open-api';
4444
import createApp from './lib/create-app';
4545
import { httpsEnforcement, requestId } from './middleware/security.middleware';
46-
import { agencyRoutes, authRoutes, requestRoutes, templateRoutes } from './routes';
46+
import { agencyRoutes, authRoutes, redactionRoutes, requestRoutes, templateRoutes } from './routes';
4747
import agenciesOpenAPIRoute from './routes/agencies';
4848
import authOpenAPIRoute from './routes/auth';
4949
import indexRoute from './routes/index.route';
@@ -148,6 +148,7 @@ api.route('/auth', authRoutes);
148148
api.route('/requests', requestRoutes);
149149
api.route('/agencies', agencyRoutes);
150150
api.route('/templates', templateRoutes);
151+
api.route('/redaction', redactionRoutes);
151152

152153
app.route('/api/v1', api);
153154

apps/api/src/lib/logger.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* Copyright (c) 2025 Foia Stream
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
123
/**
224
* @file Standalone Logger
325
* @module lib/logger
@@ -32,9 +54,11 @@ export const logger = pino(
3254
level: env.NODE_ENV === 'production' ? 'info' : 'debug',
3355
name: 'foia-stream-api',
3456
},
35-
env.NODE_ENV === 'production' ? undefined : pretty({
36-
colorize: true,
37-
translateTime: 'SYS:standard',
38-
ignore: 'pid,hostname',
39-
}),
57+
env.NODE_ENV === 'production'
58+
? undefined
59+
: pretty({
60+
colorize: true,
61+
translateTime: 'SYS:standard',
62+
ignore: 'pid,hostname',
63+
}),
4064
);

apps/api/src/middleware/rate-limit.middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,8 @@ export {
706706
KeyedThrottle,
707707
LeakyBucketLimiter,
708708
SlidingWindowCounter,
709-
throttle,
710709
TokenBucketLimiter,
710+
throttle,
711711
} from '@foia-stream/shared';
712712
// Re-export banlist functions for convenience
713713
export {

apps/api/src/middleware/security.middleware.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* Copyright (c) 2025 Foia Stream
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
123
/**
224
* @file Security Middleware
325
* @module middleware/security
@@ -79,14 +101,14 @@ export function securityHeaders(): MiddlewareHandler {
79101
// Permissions policy (formerly Feature-Policy)
80102
c.header(
81103
'Permissions-Policy',
82-
'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()'
104+
'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()',
83105
);
84106

85107
// Content Security Policy
86108
if (env.NODE_ENV === 'production') {
87109
c.header(
88110
'Content-Security-Policy',
89-
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';"
111+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';",
90112
);
91113
}
92114

0 commit comments

Comments
 (0)