Skip to content

Commit ca8bd3f

Browse files
committed
security+infra: CSP enforcing, structured logging, cleanup dead refs
Content-Security-Policy: - Promoted from Report-Only to enforcing mode (same policy, tested stable) - Added img.youtube.com to img-src for YouTube thumbnail Structured Logging: - Created app/lib/logger.ts — JSON logger with timestamp, level, route, message - Replaced all 23 console.* calls across 12 API routes with structured log.info/warn/error - Zero unstructured console calls remain in API routes - CloudWatch Logs Insights can now query by route, level, timestamp Dead Reference Cleanup: - Removed S3_MODELS_BUCKET from next.config.ts env: block (not used at runtime) - Updated README: Zustand → React useState + Context (zustand was never imported) - Updated DOCS.md: S3 SDK references corrected (removed, not used at runtime) - Updated Amazon_usage.md: S3 SDK status corrected
1 parent 9c5bd0b commit ca8bd3f

17 files changed

Lines changed: 120 additions & 32 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ AutiSense is a web application that captures behavioral biomarkers using real-ti
3737
| Framework | Next.js 16.2.2 (App Router, React 19.2.4) |
3838
| Language | TypeScript 5 |
3939
| Styling | Tailwind CSS v4 + CSS custom properties |
40-
| State | Zustand (global) + React useState (local) |
40+
| State | React useState (local) + React Context (auth) |
4141
| On-device AI | ONNX Runtime Web 1.24.2 (WebGPU/WASM) |
4242
| Face Analysis | @mediapipe/tasks-vision (478 landmarks, 52 blendshapes) |
4343
| Client Database | Dexie.js v4 (IndexedDB, 10 tables) |

app/api/auth/callback/google/route.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import { NextRequest, NextResponse } from "next/server";
1414
import { AUTH_CONFIG } from "@/app/lib/auth/config";
1515
import { upsertGoogleUser, createSessionForUser } from "@/app/lib/auth/dynamodb";
16+
import { logger } from "@/app/lib/logger";
17+
18+
const log = logger("auth/callback");
1619

1720
export async function GET(request: NextRequest) {
1821
const { searchParams } = new URL(request.url);
@@ -25,7 +28,7 @@ export async function GET(request: NextRequest) {
2528

2629
// ─── Error from Google ──────────────────────────────────────────
2730
if (error) {
28-
console.error("[auth/callback/google] OAuth error:", error);
31+
log.error("OAuth error", { error });
2932
return NextResponse.redirect(`${appUrl}/auth/login?error=${encodeURIComponent(error)}`);
3033
}
3134

@@ -56,7 +59,7 @@ export async function GET(request: NextRequest) {
5659

5760
if (!tokenResponse.ok) {
5861
const errorBody = await tokenResponse.text();
59-
console.error("[auth/callback/google] Token exchange failed:", errorBody);
62+
log.error("Token exchange failed", { error: errorBody });
6063
return NextResponse.redirect(`${appUrl}/auth/login?error=token_exchange_failed`);
6164
}
6265

@@ -74,7 +77,7 @@ export async function GET(request: NextRequest) {
7477
});
7578

7679
if (!profileResponse.ok) {
77-
console.error("[auth/callback/google] Profile fetch failed:", profileResponse.status);
80+
log.error("Profile fetch failed", { error: profileResponse.status });
7881
return NextResponse.redirect(`${appUrl}/auth/login?error=profile_fetch_failed`);
7982
}
8083

@@ -120,7 +123,7 @@ export async function GET(request: NextRequest) {
120123

121124
return response;
122125
} catch (err) {
123-
console.error("[auth/callback/google] Unexpected error:", err);
126+
log.error("Unexpected error", { error: err });
124127
return NextResponse.redirect(`${appUrl}/auth/login?error=server_error`);
125128
}
126129
}

app/api/auth/logout/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import { NextRequest, NextResponse } from "next/server";
1111
import { AUTH_CONFIG } from "@/app/lib/auth/config";
1212
import { deleteAuthSession } from "@/app/lib/auth/dynamodb";
13+
import { logger } from "@/app/lib/logger";
14+
15+
const log = logger("auth/logout");
1316

1417
export async function POST(request: NextRequest) {
1518
const token = request.cookies.get(AUTH_CONFIG.sessionCookieName)?.value;
@@ -18,7 +21,7 @@ export async function POST(request: NextRequest) {
1821
try {
1922
await deleteAuthSession(token);
2023
} catch (err) {
21-
console.error("[auth/logout] Error deleting session:", err);
24+
log.error("Error deleting session", { error: err });
2225
// Continue with logout even if DynamoDB delete fails
2326
}
2427
}

app/api/auth/session/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import { NextRequest, NextResponse } from "next/server";
99
import { AUTH_CONFIG } from "@/app/lib/auth/config";
1010
import { getAuthSession, getUserById } from "@/app/lib/auth/dynamodb";
11+
import { logger } from "@/app/lib/logger";
12+
13+
const log = logger("auth/session");
1114

1215
export async function GET(request: NextRequest) {
1316
const token = request.cookies.get(AUTH_CONFIG.sessionCookieName)?.value;
@@ -38,7 +41,7 @@ export async function GET(request: NextRequest) {
3841
authenticated: true,
3942
});
4043
} catch (err) {
41-
console.error("[auth/session] Error validating session:", err);
44+
log.error("Error validating session", { error: err });
4245
return NextResponse.json({ user: null, authenticated: false }, { status: 401 });
4346
}
4447
}

app/api/chat/conversation/route.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import {
1919
InvokeModelCommand,
2020
} from "@aws-sdk/client-bedrock-runtime";
2121
import { getAppCredentials } from "../../../lib/aws/credentials";
22+
import { logger } from "../../../lib/logger";
23+
24+
const log = logger("chat/conversation");
2225

2326
/* ------------------------------------------------------------------ */
2427
/* Types */
@@ -335,19 +338,19 @@ export async function POST(req: NextRequest) {
335338
"";
336339

337340
if (!rawText) {
338-
console.warn("[Chat] Empty response from Bedrock, using fallback");
341+
log.warn("Empty response from Bedrock, using fallback");
339342
return NextResponse.json(buildFallbackTurn(childName, turnNumber, animalPersonality));
340343
}
341344

342345
const parsed = parseAgentResponse(rawText);
343346
if (!parsed) {
344-
console.warn("[Chat] Failed to parse LLM JSON, using fallback. Raw:", rawText);
347+
log.warn("Failed to parse LLM JSON, using fallback", { raw: rawText });
345348
return NextResponse.json(buildFallbackTurn(childName, turnNumber, animalPersonality));
346349
}
347350

348351
return NextResponse.json({ ...parsed, fallback: false } satisfies ConversationResponse);
349352
} catch (err) {
350-
console.error("[Chat] Bedrock invocation failed:", err);
353+
log.error("Bedrock invocation failed", { error: err });
351354
return NextResponse.json(buildFallbackTurn(childName, turnNumber, animalPersonality));
352355
}
353356
}

app/api/chat/generate-words/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import {
1818
InvokeModelCommand,
1919
} from "@aws-sdk/client-bedrock-runtime";
2020
import { getAppCredentials } from "../../../lib/aws/credentials";
21+
import { logger } from "../../../lib/logger";
22+
23+
const log = logger("chat/generate-words");
2124

2225
/* ------------------------------------------------------------------ */
2326
/* Types */
@@ -287,7 +290,7 @@ export async function POST(request: NextRequest) {
287290
fallback: true,
288291
});
289292
} catch (err) {
290-
console.error("[generate-words] Unexpected error:", err);
293+
log.error("Unexpected error", { error: err });
291294
return NextResponse.json(
292295
{ error: "Failed to generate words" },
293296
{ status: 500 },

app/api/feed/route.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { NextRequest, NextResponse } from "next/server";
22
import { AUTH_CONFIG } from "@/app/lib/auth/config";
33
import { getAuthSession, getUserById } from "@/app/lib/auth/dynamodb";
44
import { getAppCredentials, getAppRegion } from "@/app/lib/aws/credentials";
5+
import { logger } from "@/app/lib/logger";
6+
7+
const log = logger("feed");
58

69
/**
710
* Community Feed API — DynamoDB table: autisense-feed-posts
@@ -93,7 +96,7 @@ export async function GET(request: NextRequest) {
9396
source: "dynamodb",
9497
});
9598
} catch (err) {
96-
console.error("[feed] DynamoDB GET failed, falling back:", err);
99+
log.error("DynamoDB GET failed, falling back", { error: err });
97100
dynamoFailedUntil = Date.now() + 30_000;
98101
}
99102
}
@@ -160,7 +163,7 @@ async function handleCreate(body: Record<string, unknown>, userId: string) {
160163
await docClient.send(new PutCommand({ TableName: TABLE, Item: post }));
161164
return NextResponse.json({ success: true, post: toClient(post), source: "dynamodb" });
162165
} catch (err) {
163-
console.error("[feed] DynamoDB PUT failed, falling back:", err);
166+
log.error("DynamoDB PUT failed, falling back", { error: err });
164167
dynamoFailedUntil = Date.now() + 30_000;
165168
}
166169
}
@@ -218,7 +221,7 @@ async function handleReaction(body: Record<string, unknown>, userId: string) {
218221
return NextResponse.json({ toggled: true });
219222
}
220223
} catch (err) {
221-
console.error("[feed] DynamoDB reaction failed, falling back:", err);
224+
log.error("DynamoDB reaction failed, falling back", { error: err });
222225
dynamoFailedUntil = Date.now() + 30_000;
223226
}
224227
}
@@ -264,7 +267,7 @@ async function handleDelete(body: Record<string, unknown>, userId: string) {
264267
await docClient.send(new DeleteCommand({ TableName: TABLE, Key: key }));
265268
return NextResponse.json({ success: true });
266269
} catch (err) {
267-
console.error("[feed] DynamoDB delete failed, falling back:", err);
270+
log.error("DynamoDB delete failed, falling back", { error: err });
268271
dynamoFailedUntil = Date.now() + 30_000;
269272
}
270273
}

app/api/nearby/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
*/
1010

1111
import { NextRequest, NextResponse } from "next/server";
12+
import { logger } from "@/app/lib/logger";
13+
14+
const log = logger("nearby");
1215

1316
interface NearbyResult {
1417
id: number;
@@ -117,7 +120,7 @@ export async function POST(req: NextRequest) {
117120
},
118121
);
119122
} catch (err) {
120-
console.error("[Nearby] Overpass query failed:", err);
123+
log.error("Overpass query failed", { error: err });
121124
return NextResponse.json(
122125
{ error: "Failed to query nearby facilities", results: [], source: "overpass" },
123126
{ status: 500 },

app/api/report/clinical/route.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import {
2929
} from "@aws-sdk/client-bedrock-runtime";
3030
import type { BiomarkerAggregate } from "../../../types/biomarker";
3131
import { getAppCredentials } from "../../../lib/aws/credentials";
32+
import { logger } from "../../../lib/logger";
33+
34+
const log = logger("report/clinical");
3235

3336
interface ClinicalRequestBody {
3437
sessionId: string;
@@ -271,13 +274,13 @@ export async function POST(req: NextRequest) {
271274

272275
const insights = parseInsights(text);
273276
if (!insights) {
274-
console.warn("[Report/Clinical] Could not parse AI insights, using template only");
277+
log.warn("Could not parse AI insights, using template only");
275278
return NextResponse.json({ ...baseReport, aiEnriched: false });
276279
}
277280

278281
return NextResponse.json({ ...mergeAiInsights(baseReport, insights), aiEnriched: true });
279282
} catch (err) {
280-
console.error("[Report/Clinical] Bedrock invocation failed:", err);
283+
log.error("Bedrock invocation failed", { error: err });
281284
return NextResponse.json({ ...baseReport, aiEnriched: false });
282285
}
283286
}

app/api/report/pdf/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import { NextRequest, NextResponse } from "next/server";
2323
import { PDFDocument, rgb, StandardFonts, PDFPage } from "pdf-lib";
24+
import { logger } from "../../../lib/logger";
25+
26+
const log = logger("report/pdf");
2427

2528
interface PdfRequestBody {
2629
report: string;
@@ -565,7 +568,7 @@ export async function POST(req: NextRequest) {
565568
},
566569
});
567570
} catch (err) {
568-
console.error("[Report/PDF] PDF generation failed:", err);
571+
log.error("PDF generation failed", { error: err });
569572
return NextResponse.json({ error: "Failed to generate PDF" }, { status: 500 });
570573
}
571574
}

0 commit comments

Comments
 (0)