Skip to content

Commit f13e7b8

Browse files
author
teycir
committed
feat(api): enhance security with validations and request tracking
Add HTTP method and origin validations to create-seal and seal retrieval endpoints to prevent unauthorized access. Introduce concurrent request tracking and suspicious pattern detection in seal retrieval to mitigate abuse and improve system resilience. Update changelog for version 0.4.0.
1 parent 254c2ab commit f13e7b8

9 files changed

Lines changed: 1171 additions & 36 deletions

File tree

app/api/create-seal/route.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ import {
99
validateTimestamp,
1010
} from "@/lib/validation";
1111
import { ErrorCode, createErrorResponse } from "@/lib/errors";
12+
import { validateHTTPMethod, validateOrigin } from "@/lib/security";
1213

1314
export async function POST(request: NextRequest) {
15+
if (!validateHTTPMethod(request, ["POST"])) {
16+
return jsonResponse({ error: "Method not allowed" }, 405);
17+
}
18+
19+
if (!validateOrigin(request)) {
20+
return jsonResponse({ error: "Invalid origin" }, 403);
21+
}
22+
1423
const contentLength = parseInt(request.headers.get("content-length") || "0");
1524

1625
const sizeValidation = validateRequestSize(contentLength);

app/api/seal/[id]/route.ts

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,75 @@ import { NextRequest } from 'next/server';
22
import { jsonResponse } from '@/lib/apiHandler';
33
import { createAPIRoute } from '@/lib/routeHelper';
44
import { validateSealId } from '@/lib/validation';
5-
import { isHoneypot } from '@/lib/security';
5+
import { isHoneypot, validateHTTPMethod, validateOrigin, concurrentTracker, detectSuspiciousPattern } from '@/lib/security';
66
import { logger } from '@/lib/logger';
77

88

99
export async function GET(
1010
request: NextRequest,
1111
{ params }: { params: Promise<{ id: string }> }
1212
) {
13-
return createAPIRoute(async ({ container, ip }) => {
14-
const { id: sealId } = await params;
13+
if (!validateHTTPMethod(request, ['GET'])) {
14+
return jsonResponse({ error: 'Method not allowed' }, 405);
15+
}
1516

16-
const sealIdValidation = validateSealId(sealId);
17-
if (!sealIdValidation.valid) {
18-
return jsonResponse({ error: sealIdValidation.error }, 400);
19-
}
17+
if (!validateOrigin(request)) {
18+
return jsonResponse({ error: 'Invalid origin' }, 403);
19+
}
2020

21-
if (isHoneypot(sealId)) {
22-
logger.warn('honeypot_accessed', { ip, sealId, userAgent: request.headers.get('user-agent') });
23-
return jsonResponse({
24-
id: sealId,
25-
isLocked: true,
26-
unlockTime: Date.now() + 999999999999,
27-
timeRemaining: 999999999999,
28-
});
21+
return createAPIRoute(async ({ container, ip }) => {
22+
if (!concurrentTracker.track(ip)) {
23+
return jsonResponse({ error: 'Too many concurrent requests' }, 429);
2924
}
30-
const sealService = container.sealService;
31-
const metadata = await sealService.getSeal(sealId, ip);
3225

33-
if (metadata.status === 'locked') {
26+
try {
27+
const { id: sealId } = await params;
28+
29+
const sealIdValidation = validateSealId(sealId);
30+
if (!sealIdValidation.valid) {
31+
return jsonResponse({ error: sealIdValidation.error }, 400);
32+
}
33+
34+
if (detectSuspiciousPattern(ip, sealId)) {
35+
logger.warn('suspicious_pattern', { ip, sealId });
36+
}
37+
38+
if (isHoneypot(sealId)) {
39+
logger.warn('honeypot_accessed', { ip, sealId, userAgent: request.headers.get('user-agent') });
40+
return jsonResponse({
41+
id: sealId,
42+
isLocked: true,
43+
unlockTime: Date.now() + 999999999999,
44+
timeRemaining: 999999999999,
45+
});
46+
}
47+
48+
const sealService = container.sealService;
49+
const metadata = await sealService.getSeal(sealId, ip);
50+
51+
if (metadata.status === 'locked') {
52+
return jsonResponse({
53+
id: sealId,
54+
isLocked: true,
55+
unlockTime: metadata.unlockTime,
56+
timeRemaining: metadata.unlockTime - Date.now(),
57+
});
58+
}
59+
60+
const blob = await sealService.getBlob(sealId);
61+
const bytes = new Uint8Array(blob);
62+
const blobBase64 = btoa(String.fromCharCode(...bytes));
63+
3464
return jsonResponse({
3565
id: sealId,
36-
isLocked: true,
66+
isLocked: false,
3767
unlockTime: metadata.unlockTime,
38-
timeRemaining: metadata.unlockTime - Date.now(),
68+
keyB: metadata.keyB,
69+
iv: metadata.iv,
70+
encryptedBlob: blobBase64,
3971
});
72+
} finally {
73+
concurrentTracker.release(ip);
4074
}
41-
42-
const blob = await sealService.getBlob(sealId);
43-
const bytes = new Uint8Array(blob);
44-
const blobBase64 = btoa(String.fromCharCode(...bytes));
45-
46-
return jsonResponse({
47-
id: sealId,
48-
isLocked: false,
49-
unlockTime: metadata.unlockTime,
50-
keyB: metadata.keyB,
51-
iv: metadata.iv,
52-
encryptedBlob: blobBase64,
53-
});
5475
}, { rateLimit: { limit: 20, window: 60000 } })(request);
5576
}

docs/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.4.0] - 2025-12-22
11+
12+
### Added
13+
- HTTP method validation on all endpoints
14+
- Request origin validation
15+
- Concurrent request limiting (5 per IP)
16+
- Suspicious pattern detection (sequential seal IDs)
17+
- Seal age validation (2 year maximum)
18+
19+
### Security
20+
- Method-based access control (GET/POST only)
21+
- Origin header validation
22+
- Concurrent request tracking and limiting
23+
- Sequential access pattern detection
24+
- Enhanced logging for suspicious activity
25+
1026
## [0.3.0] - 2025-12-22
1127

1228
### Added

0 commit comments

Comments
 (0)