diff --git a/apps/cli/src/commands/setup.ts b/apps/cli/src/commands/setup.ts index 8716a25..213cb89 100644 --- a/apps/cli/src/commands/setup.ts +++ b/apps/cli/src/commands/setup.ts @@ -219,9 +219,9 @@ async function setupRouter(): Promise { const router: ShannonConfig['router'] = { default: defaultModel }; if (routerProvider === 'openai') { - router!.openai_key = apiKey; + router.openai_key = apiKey; } else { - router!.openrouter_key = apiKey; + router.openrouter_key = apiKey; } return { router }; diff --git a/apps/cli/src/commands/start.ts b/apps/cli/src/commands/start.ts index cd8aa4e..c049522 100644 --- a/apps/cli/src/commands/start.ts +++ b/apps/cli/src/commands/start.ts @@ -65,11 +65,8 @@ export function start(args: StartArgs): void { const containerName = `shannon-worker-${suffix}`; // 8. Generate workspace name if not provided - let workspace = args.workspace; - if (!workspace) { - const hostname = new URL(args.url).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); - workspace = `${hostname}_shannon-${Date.now()}`; - } + const workspace = + args.workspace ?? `${new URL(args.url).hostname.replace(/[^a-zA-Z0-9-]/g, '-')}_shannon-${Date.now()}`; // 9. Resolve credentials const credentialsDir = getCredentialsDir(); @@ -142,7 +139,7 @@ export function start(args: StartArgs): void { // Clear waiting line and show info process.stdout.write('\r\x1b[K'); - printInfo(args, useRouter, workspace!, workflowId, repo.hostPath, workspacesDir); + printInfo(args, useRouter, workspace, workflowId, repo.hostPath, workspacesDir); return; } } catch { diff --git a/apps/worker/src/ai/claude-executor.ts b/apps/worker/src/ai/claude-executor.ts index 1612589..3c159c5 100644 --- a/apps/worker/src/ai/claude-executor.ts +++ b/apps/worker/src/ai/claude-executor.ts @@ -88,7 +88,10 @@ function buildMcpServers( // NOTE: Explicit allowlist — the Playwright MCP subprocess must not inherit // secrets (API keys, AWS tokens) from the parent process. const MCP_ENV_ALLOWLIST = [ - 'PATH', 'HOME', 'NODE_PATH', 'DISPLAY', + 'PATH', + 'HOME', + 'NODE_PATH', + 'DISPLAY', 'PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH', ] as const; @@ -98,8 +101,9 @@ function buildMcpServers( }; for (const key of MCP_ENV_ALLOWLIST) { - if (process.env[key]) { - envVars[key] = process.env[key]!; + const val = process.env[key]; + if (val) { + envVars[key] = val; } } @@ -252,8 +256,9 @@ export async function runClaudePrompt( 'ANTHROPIC_LARGE_MODEL', ]; for (const name of passthroughVars) { - if (process.env[name]) { - sdkEnv[name] = process.env[name]!; + const val = process.env[name]; + if (val) { + sdkEnv[name] = val; } } diff --git a/apps/worker/src/ai/output-formatters.ts b/apps/worker/src/ai/output-formatters.ts index 1e44280..8a0b2ed 100644 --- a/apps/worker/src/ai/output-formatters.ts +++ b/apps/worker/src/ai/output-formatters.ts @@ -89,14 +89,14 @@ function summarizeTodoUpdate(input: ToolCallInput | undefined): string | null { const inProgress = todos.filter((t) => t.status === 'in_progress'); // Show recently completed tasks - if (completed.length > 0) { - const recent = completed[completed.length - 1]!; + const recent = completed.at(-1); + if (recent) { return `✅ ${recent.content}`; } // Show current in-progress task - if (inProgress.length > 0) { - const current = inProgress[0]!; + const current = inProgress.at(0); + if (current) { return `🔄 ${current.content}`; } diff --git a/apps/worker/src/audit/metrics-tracker.ts b/apps/worker/src/audit/metrics-tracker.ts index f88a6c1..1ce9ea4 100644 --- a/apps/worker/src/audit/metrics-tracker.ts +++ b/apps/worker/src/audit/metrics-tracker.ts @@ -336,6 +336,7 @@ export class MetricsTracker { // Calculate metrics per phase const phaseMetrics: Record = {}; + // biome-ignore lint/style/noNonNullAssertion: called from recalculateAggregations which guards this.data const totalDuration = this.data!.metrics.total_duration_ms; for (const [phaseName, agentList] of Object.entries(phases)) { diff --git a/apps/worker/src/services/prompt-manager.ts b/apps/worker/src/services/prompt-manager.ts index 027e918..0876431 100644 --- a/apps/worker/src/services/prompt-manager.ts +++ b/apps/worker/src/services/prompt-manager.ts @@ -37,7 +37,7 @@ async function buildLoginInstructions(authentication: Authentication, logger: Ac const getSection = (content: string, sectionName: string): string => { const regex = new RegExp(`([\\s\\S]*?)`, 'g'); const match = regex.exec(content); - return match ? match[1]!.trim() : ''; + return match?.[1]?.trim() ?? ''; }; // 2. Extract sections based on login type @@ -101,14 +101,13 @@ async function processIncludes(content: string, baseDir: string): Promise { - const includePath = path.resolve(baseDir, match[1]!); + const rawPath = match[1] ?? ''; + const includePath = path.resolve(baseDir, rawPath); if (!includePath.startsWith(resolvedBase + path.sep) && includePath !== resolvedBase) { - throw new PentestError( - `Path traversal detected in @include(): ${match[1]}`, - 'prompt', - false, - { includePath, baseDir: resolvedBase }, - ); + throw new PentestError(`Path traversal detected in @include(): ${rawPath}`, 'prompt', false, { + includePath, + baseDir: resolvedBase, + }); } const sharedContent = await fs.readFile(includePath, 'utf8'); return { diff --git a/apps/worker/src/services/queue-validation.ts b/apps/worker/src/services/queue-validation.ts index 86a5a04..0bb7be4 100644 --- a/apps/worker/src/services/queue-validation.ts +++ b/apps/worker/src/services/queue-validation.ts @@ -243,7 +243,7 @@ const validateQueueContent = async ( return Object.freeze({ ...pathsWithExistence, - queueData: queueValidation.data!, + queueData: queueValidation.data as QueueData, }); } catch (readError) { return { diff --git a/apps/worker/src/utils/functional.ts b/apps/worker/src/utils/functional.ts index 3019ea5..261f2ec 100644 --- a/apps/worker/src/utils/functional.ts +++ b/apps/worker/src/utils/functional.ts @@ -10,7 +10,7 @@ * Generic functional composition patterns for async operations. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: pipeline functions need flexible typing for composition type PipelineFunction = (x: any) => any | Promise; /** diff --git a/packages/mcp-server/src/tools/generate-totp.ts b/packages/mcp-server/src/tools/generate-totp.ts index 82c9ca4..e923e7e 100644 --- a/packages/mcp-server/src/tools/generate-totp.ts +++ b/packages/mcp-server/src/tools/generate-totp.ts @@ -48,13 +48,14 @@ function generateHOTP(secret: string, counter: number, digits: number = 6): stri hmac.update(counterBuffer); const hash = hmac.digest(); - // Dynamic truncation - const offset = hash[hash.length - 1]! & 0x0f; + // Dynamic truncation (SHA-1 always produces 20 bytes) + const lastByte = hash[hash.length - 1] ?? 0; + const offset = lastByte & 0x0f; const code = - ((hash[offset]! & 0x7f) << 24) | - ((hash[offset + 1]! & 0xff) << 16) | - ((hash[offset + 2]! & 0xff) << 8) | - (hash[offset + 3]! & 0xff); + (((hash[offset] ?? 0) & 0x7f) << 24) | + (((hash[offset + 1] ?? 0) & 0xff) << 16) | + (((hash[offset + 2] ?? 0) & 0xff) << 8) | + ((hash[offset + 3] ?? 0) & 0xff); // Generate digits const otp = (code % 10 ** digits).toString().padStart(digits, '0');