code security

This commit is contained in:
overcuriousity
2025-07-19 15:30:12 +02:00
parent e29d10cf81
commit 20e9e5e5ae
8 changed files with 112 additions and 38 deletions

View File

@@ -14,7 +14,7 @@ function getEnv(key: string): string {
return value;
}
const SECRET_KEY = new TextEncoder().encode(getEnv('OIDC_CLIENT_SECRET'));
const SECRET_KEY = new TextEncoder().encode(getEnv('AUTH_SECRET'));
const SESSION_DURATION = 6 * 60 * 60; // 6 hours in seconds
export interface SessionData {
@@ -74,12 +74,13 @@ export function getSessionFromRequest(request: Request): string | null {
// Create session cookie
export function createSessionCookie(token: string): string {
const publicBaseUrl = getEnv('PUBLIC_BASE_URL');
const isSecure = publicBaseUrl.startsWith('https://');
const isProduction = process.env.NODE_ENV === 'production';
const isSecure = publicBaseUrl.startsWith('https://') || isProduction;
return serialize('session', token, {
httpOnly: true,
secure: isSecure,
sameSite: 'lax',
sameSite: 'strict', // More secure than 'lax'
maxAge: SESSION_DURATION,
path: '/'
});
@@ -127,7 +128,7 @@ export async function exchangeCodeForTokens(code: string): Promise<any> {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(`${clientId}:${clientSecret}`)}`
'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`
},
body: new URLSearchParams({
grant_type: 'authorization_code',

View File

@@ -1,6 +1,43 @@
import { promises as fs } from 'fs';
import { load } from 'js-yaml';
import path from 'path';
import { z } from 'zod';
const ToolSchema = z.object({
name: z.string(),
description: z.string(),
domains: z.array(z.string()).optional().nullable().default([]),
phases: z.array(z.string()).optional().nullable().default([]),
platforms: z.array(z.string()).default([]),
skillLevel: z.string(),
url: z.string(),
license: z.string(),
tags: z.array(z.string()).default([]),
// Optional fields that can be null, undefined, or empty
projectUrl: z.string().optional().nullable(),
knowledgebase: z.boolean().optional().nullable(),
statusUrl: z.string().optional().nullable(),
accessType: z.string().optional(),
'domain-agnostic-software': z.array(z.string()).optional().nullable(),
});
const ToolsDataSchema = z.object({
tools: z.array(ToolSchema),
domains: z.array(z.object({
id: z.string(),
name: z.string()
})),
phases: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string().optional()
})),
'domain-agnostic-software': z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string().optional()
})).optional().default([]),
});
interface ToolsData {
tools: any[];
@@ -49,7 +86,14 @@ async function loadRawData(): Promise<ToolsData> {
if (!cachedData) {
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
const yamlContent = await fs.readFile(yamlPath, 'utf8');
cachedData = load(yamlContent) as ToolsData;
const rawData = load(yamlContent);
try {
cachedData = ToolsDataSchema.parse(rawData);
} catch (error) {
console.error('YAML validation failed:', error);
throw new Error('Invalid tools.yaml structure');
}
}
return cachedData;
}