diff --git a/Dockerfile b/Dockerfile index c84d7a2..3c93ba6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,16 +10,18 @@ COPY . . RUN npm run build FROM node:20-bookworm-slim AS runner -WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 -COPY package*.json ./ +USER node +WORKDIR /app + +COPY --chown=node:node package*.json ./ RUN npm ci --omit=dev -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/public ./public -COPY --from=builder /app/next.config.mjs ./next.config.mjs +COPY --chown=node:node --from=builder /app/.next ./.next +COPY --chown=node:node --from=builder /app/public ./public +COPY --chown=node:node --from=builder /app/next.config.mjs ./next.config.mjs EXPOSE 3000 CMD ["npm", "run", "start"] diff --git a/README.md b/README.md index a43e816..eb26317 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Open [http://localhost:3000/dashboard](http://localhost:3000/dashboard) to see t AgentOS is an autonomous prompt optimization operating system — a self-evolving AI execution layer that: -- �� **Optimizes prompts** through a 7-agent swarm pipeline +- 🤖 **Optimizes prompts** through a 7-agent swarm pipeline - ⚡ **Executes across multiple AI models** (OpenAI, Claude, Gemini, Llama) - 📊 **Scores and selects** the best outputs via weighted evaluation - 🔁 **Evolves prompts** via feedback loops (up to 10 iterations) diff --git a/core/wasm/wasmRunner.ts b/core/wasm/wasmRunner.ts index f105fc2..1d7c346 100644 --- a/core/wasm/wasmRunner.ts +++ b/core/wasm/wasmRunner.ts @@ -28,8 +28,22 @@ export class WasmRunner { constructor(options: WasmRunnerOptions = {}) { this.now = options.now ?? Date.now; - this.defaultTimeoutMs = options.defaultTimeoutMs ?? 10_000; - this.maxTimeoutMs = options.maxTimeoutMs ?? 60_000; + + const defaultTimeoutMs = options.defaultTimeoutMs ?? 10_000; + const maxTimeoutMs = options.maxTimeoutMs ?? 60_000; + + if (!Number.isFinite(defaultTimeoutMs) || defaultTimeoutMs <= 0) { + throw new Error('WasmRunner: defaultTimeoutMs must be a positive finite number'); + } + if (!Number.isFinite(maxTimeoutMs) || maxTimeoutMs <= 0) { + throw new Error('WasmRunner: maxTimeoutMs must be a positive finite number'); + } + if (maxTimeoutMs < defaultTimeoutMs) { + throw new Error('WasmRunner: maxTimeoutMs must be >= defaultTimeoutMs'); + } + + this.defaultTimeoutMs = defaultTimeoutMs; + this.maxTimeoutMs = maxTimeoutMs; } /** diff --git a/core/wasm/workerPool.ts b/core/wasm/workerPool.ts index 714b365..cfa2111 100644 --- a/core/wasm/workerPool.ts +++ b/core/wasm/workerPool.ts @@ -25,7 +25,12 @@ export class WorkerPool { } this.concurrency = concurrency; - this.maxQueueSize = normalized.maxQueueSize ?? concurrency * 100; + + const rawMaxQueueSize = normalized.maxQueueSize ?? concurrency * 100; + if (!Number.isSafeInteger(rawMaxQueueSize) || rawMaxQueueSize < 0) { + throw new Error('WorkerPool: maxQueueSize must be a non-negative safe integer'); + } + this.maxQueueSize = rawMaxQueueSize; this.runners = Array.from({ length: concurrency }, () => new WasmRunner()); } diff --git a/deploy/k8s/base/deployment.yaml b/deploy/k8s/base/deployment.yaml index 6176dcf..21a57be 100644 --- a/deploy/k8s/base/deployment.yaml +++ b/deploy/k8s/base/deployment.yaml @@ -18,6 +18,8 @@ spec: spec: securityContext: runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 seccompProfile: type: RuntimeDefault containers: diff --git a/tests/wasm.determinism.test.ts b/tests/wasm.determinism.test.ts index f243c86..7e6cedb 100644 --- a/tests/wasm.determinism.test.ts +++ b/tests/wasm.determinism.test.ts @@ -61,3 +61,38 @@ describe('WasmRunner determinism', () => { expect(result.error).toContain('timed out after 40ms'); }); }); + +describe('WasmRunner constructor validation', () => { + it('rejects non-positive or non-finite defaultTimeoutMs', () => { + expect(() => new WasmRunner({ defaultTimeoutMs: 0 })).toThrow( + 'WasmRunner: defaultTimeoutMs must be a positive finite number', + ); + expect(() => new WasmRunner({ defaultTimeoutMs: -1 })).toThrow( + 'WasmRunner: defaultTimeoutMs must be a positive finite number', + ); + expect(() => new WasmRunner({ defaultTimeoutMs: Infinity })).toThrow( + 'WasmRunner: defaultTimeoutMs must be a positive finite number', + ); + expect(() => new WasmRunner({ defaultTimeoutMs: NaN })).toThrow( + 'WasmRunner: defaultTimeoutMs must be a positive finite number', + ); + }); + + it('rejects non-positive or non-finite maxTimeoutMs', () => { + expect(() => new WasmRunner({ maxTimeoutMs: 0 })).toThrow( + 'WasmRunner: maxTimeoutMs must be a positive finite number', + ); + expect(() => new WasmRunner({ maxTimeoutMs: -1 })).toThrow( + 'WasmRunner: maxTimeoutMs must be a positive finite number', + ); + expect(() => new WasmRunner({ maxTimeoutMs: Infinity })).toThrow( + 'WasmRunner: maxTimeoutMs must be a positive finite number', + ); + }); + + it('rejects maxTimeoutMs less than defaultTimeoutMs', () => { + expect(() => new WasmRunner({ defaultTimeoutMs: 5_000, maxTimeoutMs: 1_000 })).toThrow( + 'WasmRunner: maxTimeoutMs must be >= defaultTimeoutMs', + ); + }); +}); diff --git a/tests/workerPool.load.test.ts b/tests/workerPool.load.test.ts index d5f6314..736baf3 100644 --- a/tests/workerPool.load.test.ts +++ b/tests/workerPool.load.test.ts @@ -63,4 +63,19 @@ describe('WorkerPool load and scaling', () => { 'WorkerPool: concurrency must be a positive integer', ); }); + + it('rejects invalid maxQueueSize configuration', () => { + expect(() => new WorkerPool({ concurrency: 2, maxQueueSize: -1 })).toThrow( + 'WorkerPool: maxQueueSize must be a non-negative safe integer', + ); + expect(() => new WorkerPool({ concurrency: 2, maxQueueSize: NaN })).toThrow( + 'WorkerPool: maxQueueSize must be a non-negative safe integer', + ); + expect(() => new WorkerPool({ concurrency: 2, maxQueueSize: Infinity })).toThrow( + 'WorkerPool: maxQueueSize must be a non-negative safe integer', + ); + expect(() => new WorkerPool({ concurrency: 2, maxQueueSize: 1.5 })).toThrow( + 'WorkerPool: maxQueueSize must be a non-negative safe integer', + ); + }); });