// npm 패키지
@ottocode/server
HTTP API server for ottocode
버전
120
메인테이너
1
최초 publish
2026-02-04
publisher
nitishxyz
tarball
928,548 B
AUTO-PUBLISHED·2개 버전 인덱싱됨·최근 publish: 2026-06-04
// exfil path
what is read → where it shipssteals
- ● npm token
- ● GitHub PAT
- ● Chromium logins
sends to
(no destination string extracted — payload may be dynamic / obfuscated)
evidence in excerpt
> import { spawn } from 'node:child_process';// offending code· @0.1.298· 3 files flagged
- @0.1.298··AUTO-PUBLISHED·publisher: nitishxyzheuristic 64/100static flags 6llm skippednew-publisher:15dmature-packagepublisher-multi-name-burst:7publisher-version-pump:18reads-env-varschild-process-spawnreads-npmrcreads-github-tokensbase64-decodereads-chromium-creds
// offending code· 3 files flaggedpatterns: 6
--- package/src/tools/adapter/execution.ts (excerpt) --- import type { Tool } from 'ai'; import { shellExecutorContext, type ShellExecutor } from '@ottocode/sdk'; import { getAugmentedPath } from '@ottocode/sdk/tools/bin-manager'; import { appendTailLines } from '@ottocode/sdk/tools/builtin/shell'; import { createToolError, type ToolResponse } from '@ottocode/sdk/tools/error'; import { spawn } from 'node:child_process'; import type { ToolAdapterContext } from '../../runtime/tools/context.ts'; import { requestSecureInput } from '../../runtime/tools/secure-input.ts'; import { detectSecurePrompt, normalizeSudoCommand, } from '../../runtime/tools/secure-prompt.ts'; import { attachTerminalSecureInput } from '../../runtime/tools/terminal-secure-input.ts'; import { getCwd, joinRelative, setCwd } from '../../runtime/utils/cwd.ts'; import { publishToolDelta } from './events.ts'; type ToolExecuteSignature = Tool['execute'] extends ( input: infer Input, options: infer Options, ) => infer Result ? { input: Input; options: Options; result: Result } : { input: unknown; options: unknown; result: unknown }; type ToolExecuteInput = ToolExecuteSignature['input']; type ToolExecuteOptions = ToolExecuteSignature['options'] extends never ? undefined : ToolExecuteSignature['options']; type ToolExecuteReturn = ToolExecuteSignature['result']; type ShellResult = ToolResponse<{ exitCode: number; stdout: string; stderr: string; outputMode?: 'full' | 'tail'; tailLines?: number; }>; type SecureShellStreamChunk = | { channel: 'output'; delta: str --- package/src/runtime/utils/cwd.ts (excerpt) --- const cwdMap = new Map<string, string>(); export function getCwd(sessionId: string): string { return cwdMap.get(sessionId) ?? '.'; } export function setCwd(sessionId: string, cwd: string) { cwdMap.set(sessionId, cwd || '.'); } // normalize relative path like './a/../b' -> 'b', never escapes above '.' export function normalizeRelative(path: string): string { const parts = path.replace(/\\/g, '/').split('/'); const stack: string[] = []; for (const part of parts) { if (!part || part === '.') continue; if (part === '..') { if (stack.length > 0) stack.pop(); continue; } stack.push(part); } return stack.length ? stack.join('/') : '.'; } export function joinRelative(base: string, p: string): string { if (!p || p === '.') return base || '.'; // Expand tilde to home directory if present const home = process.env.HOME || process.env.USERPROFILE || ''; if (p === '~' && home) p = home; else if (p.startsWith('~/') && home) p = `${home}/${p.slice(2)}`; // If target is absolute, preserve it as absolute if (p.startsWith('/')) { // Canonicalize: collapse //, resolve . and .. without escaping above root const parts = p.replace(/\\/g, '/').split('/'); const stack: string[] = []; for (const part of parts) { if (!part || part === '.') continue; if (part === '..') { if (stack.length > 0) stack.pop(); continue; } stack.push(part); } return `/${stack.join('/')}` || '/'; } // If base is absolute, join and return absolute const baseI --- package/src/runtime/tools/guards.ts (excerpt) --- import { resolve as resolvePath } from 'node:path'; export type GuardAction = | { type: 'block'; reason: string } | { type: 'approve'; reason: string } | { type: 'allow' }; export type GuardContext = { projectRoot?: string; }; export function guardToolCall( toolName: string, args: unknown, context: GuardContext = {}, ): GuardAction { const a = (args ?? {}) as Record<string, unknown>; switch (toolName) { case 'shell': case 'bash': return guardShellCommand(String(a.cmd ?? '')); case 'terminal': return guardTerminal(a); case 'read': return guardReadPath(String(a.path ?? ''), context.projectRoot); case 'write': case 'copy_into': return guardWritePath(toolName, a); default: return { type: 'allow' }; } } function guardShellCommand(cmd: string): GuardAction { const n = cmd.trim(); if (!n) return { type: 'allow' }; const blocked = checkBlockedCommand(n); if (blocked) return { type: 'block', reason: blocked }; const approval = checkApprovalCommand(n); if (approval) return { type: 'approve', reason: approval }; return { type: 'allow' }; } function checkBlockedCommand(cmd: string): string | null { if (isRecursiveDeleteRoot(cmd)) return 'Recursive delete of root filesystem'; if (isRecursiveDeleteHome(cmd)) return 'Recursive delete of home directory'; if (isForkBomb(cmd)) return 'Fork bomb detected'; if (isFilesystemFormat(cmd)) return 'Filesystem format command'; if (isRawDiskWrite(cmd)) return 'Raw disk write operation'; retur
