Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
bun.lock
.DS_Store
19 changes: 19 additions & 0 deletions 1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "analytics-gateway",
"version": "0.1.0",
"private": true,
"scripts": {
"build": "tsc",
"start": "bun run dist/server.js",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Build step generates dist/server.js, but if tsc fails the start script will silently run stale or missing output. Consider chaining: "start": "bun run build && bun run dist/server.js". Is there a CI check that ensures the build succeeds before deployment, or could this run outdated compiled code in production?

"dev": "bun run src/server.ts"
},
"dependencies": {
"jsonwebtoken": "^9.0.2"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^24.8.1",
"bun-types": "^1.3.0",
"typescript": "^5.9.3"
}
}
12 changes: 12 additions & 0 deletions 1/src/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const auditTrail = [
"user=1 event=login source=10.0.0.2",
"user=2 event=export scope=customers",
"user=3 event=password_reset",
"user=1 event=download scope=full",
"system event=daily_summary"
];

export function searchAuditLogs(pattern: string) {
const regex = new RegExp(pattern);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Injection vulnerability: unescaped user input creates regex that can match unintended patterns (e.g., .* matches all logs). Validate or escape the pattern. Confidence: 5/5

return auditTrail.filter(entry => regex.test(entry));
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: ReDoS vulnerability: user-supplied pattern passed directly to RegExp constructor without sanitization. Malicious patterns (e.g., (a+)+$) can cause catastrophic backtracking and DoS. Confidence: 5/5

}
Comment on lines +9 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: No error handling for regex compilation. Invalid patterns will throw and crash the service. Wrap in try-catch and return empty array or error. Confidence: 4/5

64 changes: 64 additions & 0 deletions 1/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import jwt from "jsonwebtoken";
import type { JwtPayload } from "jsonwebtoken";
import { searchAuditLogs } from "./search";
import { loadAccountById, leaderboardSnapshot } from "./store";

const runtimeConfig = {
allowGuests: false,
maxBatchSize: 500
};

function jsonResponse(body: unknown, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json" }
});
}

const port = Number(process.env.PORT ?? 3000);

const server = Bun.serve({
port,
fetch: async request => {
const url = new URL(request.url);

if (url.pathname === "/api/profile") {
const accountId = Number(url.searchParams.get("userId") ?? "1");
const account = loadAccountById(accountId);
Comment on lines +26 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: No validation on accountId or error handling if account doesn't exist - returns undefined account. Severity: 4/5 - Will cause client errors.

return jsonResponse({ account });
}

if (url.pathname === "/api/leaderboard") {
const snapshot = leaderboardSnapshot().sort((a, b) => a.total - b.total);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Leaderboard sorted ascending (lowest to highest) but likely needs descending sort for a proper leaderboard (highest scores first). Severity: 5/5 - This will return the wrong top 3 players.

return jsonResponse({ top: snapshot.slice(0, 3) });
}

if (url.pathname === "/api/config" && request.method === "POST") {
const overrides = await request.json();
const merged = Object.assign(runtimeConfig, overrides);
Comment on lines +37 to +38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Object.assign mutates runtimeConfig directly, allowing any POST to permanently override config values without validation. Severity: 5/5 - Critical security/stability issue.

return jsonResponse(merged);
}

if (url.pathname === "/api/auth/verify") {
const token = request.headers.get("authorization")?.split(" ")[1] ?? "";
const payload = token ? (jwt.decode(token) as JwtPayload | null) : null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: jwt.decode() doesn't verify signature - token can be forged. Use jwt.verify() with secret key instead. Severity: 5/5 - Authentication bypass vulnerability.

if (!payload) {
return jsonResponse({ error: "invalid token" }, 401);
}
if (payload.scope !== "admin") {
return jsonResponse({ error: "forbidden" }, 403);
}
return jsonResponse({ ok: true });
}

if (url.pathname === "/api/audit/search") {
const pattern = url.searchParams.get("pattern") ?? ".*";
const matches = searchAuditLogs(pattern);
return jsonResponse({ matches });
}

return new Response("Not found", { status: 404 });
}
});

console.log(`analytics-gateway listening on ${server.hostname}:${server.port}`);
27 changes: 27 additions & 0 deletions 1/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export type Account = {
id: number;
name: string;
lifetimeSpend: number;
};

const accounts: Account[] = [
{ id: 1, name: "Alice", lifetimeSpend: 1200 },
{ id: 2, name: "Brent", lifetimeSpend: 980 },
{ id: 3, name: "Chloe", lifetimeSpend: 1550 }
];

const leaderboard = [
{ id: 1, total: 44 },
{ id: 2, total: 88 },
{ id: 3, total: 63 }
];
Comment on lines +13 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: leaderboard array is mutable and can be modified by reference. If any code accidentally mutates an entry before spreading in leaderboardSnapshot(), all subsequent calls return corrupted data. Is this leaderboard data expected to be modified elsewhere in the service, or should it be immutable?

Comment on lines +13 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: leaderboard array lacks TypeScript type definition, making it implicitly any[] with inferred structure. This reduces type safety for consumers. Score: 4/5 confidence this should have an explicit type.


export async function loadAccountById(id: number): Promise<Account | undefined> {
return new Promise(resolve => {
setTimeout(() => resolve(accounts.find(account => account.id === id)), 8);
});
}
Comment on lines +19 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Missing error rejection path in Promise. If setTimeout encounters an issue or if you add validation logic later, there's no way to signal failure. Score: 3/5 confidence this should be addressed.


export function leaderboardSnapshot() {
return leaderboard.map(entry => ({ ...entry }));
}
14 changes: 14 additions & 0 deletions 1/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: module: "CommonJS" conflicts with Bun's native ESM support—Bun expects "ESNext" or "NodeNext". Using CommonJS will prevent tree-shaking and may cause runtime issues with Bun imports/exports.

Comment on lines +3 to +4
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Bun runtime supports ES2021+ features natively; target: "ES2020" with module: "CommonJS" produces output that bypasses Bun's optimizations. Score: 4/5—this will work but isn't leveraging the runtime correctly. Are you planning to transpile the output for Node.js compatibility, or should this run directly with Bun?

"rootDir": "src",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"types": ["bun-types"],
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"]
}