Download packages/agent-core-v2/src/tool/path-access.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 10 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/src/tool/path-access.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/agent-core-v2/src/tool/path-access.ts
-
curl -L -o path-access.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/src/tool/path-access.ts
10 kB
| import * as pathe from 'pathe'; | |
| import { | |
| getShellPathBridge, | |
| translateShellDrivePath, | |
| type ShellPathBridge, | |
| } from '#/_base/execEnv/shellPathBridge'; | |
| import type { IHostEnvironment } from '#/os/interface/hostEnvironment'; | |
| export interface WorkspaceConfig { | |
| readonly workspaceDir: string; | |
| readonly additionalDirs: readonly string[]; | |
| } | |
| const SENSITIVE_BASENAMES = new Set<string>([ | |
| '.env', | |
| 'id_rsa', | |
| 'id_ed25519', | |
| 'id_ecdsa', | |
| 'credentials', | |
| ]); | |
| const SENSITIVE_PATH_SUFFIXES = [ | |
| ['.aws', 'credentials'], | |
| ['.gcp', 'credentials'], | |
| ]; | |
| const ENV_PREFIX = '.env.'; | |
| const ENV_EXEMPTIONS = new Set<string>(['.env.example', '.env.sample', '.env.template']); | |
| const SENSITIVE_BASENAME_PREFIXES = ['id_rsa', 'id_ed25519', 'id_ecdsa', 'credentials']; | |
| const PUBLIC_KEY_BASENAMES = new Set<string>(['id_rsa.pub', 'id_ed25519.pub', 'id_ecdsa.pub']); | |
| export const SENSITIVE_DOT_VARIANT_SUFFIXES = [ | |
| '.bak', | |
| '.backup', | |
| '.copy', | |
| '.disabled', | |
| '.key', | |
| '.old', | |
| '.orig', | |
| '.pem', | |
| '.save', | |
| '.tmp', | |
| ] as const; | |
| const SENSITIVE_DOT_VARIANT_SUFFIX_SET = new Set<string>(SENSITIVE_DOT_VARIANT_SUFFIXES); | |
| function comparable(path: string): string { | |
| return path.toLowerCase(); | |
| } | |
| export function isSensitiveFile(path: string): boolean { | |
| const name = pathe.basename(path); | |
| const comparableName = comparable(name); | |
| const comparablePath = comparable(path); | |
| if (ENV_EXEMPTIONS.has(comparableName)) return false; | |
| if (PUBLIC_KEY_BASENAMES.has(comparableName)) return false; | |
| if (SENSITIVE_BASENAMES.has(comparableName)) return true; | |
| if (comparableName.startsWith(ENV_PREFIX)) return true; | |
| for (const prefix of SENSITIVE_BASENAME_PREFIXES) { | |
| if (comparableName === prefix) return true; | |
| if (comparableName.length > prefix.length && comparableName.startsWith(prefix)) { | |
| const suffix = comparableName.slice(prefix.length); | |
| const next = suffix[0]; | |
| if (next === '-' || next === '_') return true; | |
| if (next === '.' && SENSITIVE_DOT_VARIANT_SUFFIX_SET.has(suffix)) return true; | |
| } | |
| } | |
| for (const suffixParts of SENSITIVE_PATH_SUFFIXES) { | |
| const suffix = suffixParts.join('/'); | |
| const comparableSuffix = comparable(suffix); | |
| if ( | |
| comparablePath.endsWith(`/${comparableSuffix}`) || | |
| comparablePath.includes(`/${comparableSuffix}/`) | |
| ) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| export type PathClass = 'posix' | 'win32'; | |
| export type PathSecurityCode = 'PATH_OUTSIDE_WORKSPACE' | 'PATH_SENSITIVE' | 'PATH_INVALID'; | |
| export type PathAccessOperation = 'read' | 'write' | 'search'; | |
| export type WorkspaceGuardMode = 'absolute-outside-allowed' | 'disabled'; | |
| export interface WorkspaceAccessPolicy { | |
| readonly guardMode: WorkspaceGuardMode; | |
| readonly checkSensitive: boolean; | |
| } | |
| export const DEFAULT_WORKSPACE_ACCESS_POLICY: WorkspaceAccessPolicy = { | |
| guardMode: 'absolute-outside-allowed', | |
| checkSensitive: true, | |
| }; | |
| export interface PathAccess { | |
| readonly path: string; | |
| readonly outsideWorkspace: boolean; | |
| } | |
| export class PathSecurityError extends Error { | |
| readonly code: PathSecurityCode; | |
| readonly rawPath: string; | |
| readonly canonicalPath: string; | |
| constructor(code: PathSecurityCode, rawPath: string, canonicalPath: string, message: string) { | |
| super(message); | |
| this.name = 'PathSecurityError'; | |
| this.code = code; | |
| this.rawPath = rawPath; | |
| this.canonicalPath = canonicalPath; | |
| } | |
| } | |
| const DEFAULT_PATH_CLASS: PathClass = process.platform === 'win32' ? 'win32' : 'posix'; | |
| function isWin32DriveRelative(path: string): boolean { | |
| return /^[A-Za-z]:(?:$|[^\\/])/.test(path); | |
| } | |
| export function normalizeUserPath(path: string, pathClass: PathClass = DEFAULT_PATH_CLASS): string { | |
| return pathClass === 'win32' ? translateShellDrivePath(path) : path; | |
| } | |
| function expandUserPath(path: string, homeDir: string | undefined, pathClass: PathClass): string { | |
| if (homeDir === undefined) return path; | |
| if (path === '~') return homeDir; | |
| if (path.startsWith('~/') || (pathClass === 'win32' && path.startsWith('~\\'))) { | |
| return pathe.join(homeDir, path.slice(2)); | |
| } | |
| return path; | |
| } | |
| export function canonicalizePath( | |
| path: string, | |
| cwd: string, | |
| pathClass: PathClass = DEFAULT_PATH_CLASS, | |
| ): string { | |
| if (path === '') { | |
| throw new PathSecurityError('PATH_INVALID', path, path, 'Path cannot be empty'); | |
| } | |
| const normalizedPath = normalizeUserPath(path, pathClass); | |
| if (pathClass === 'win32' && isWin32DriveRelative(normalizedPath)) { | |
| throw new PathSecurityError( | |
| 'PATH_INVALID', | |
| path, | |
| normalizedPath, | |
| `"${path}" is a drive-relative Windows path. Use an absolute path like C:\\path or a path relative to the working directory.`, | |
| ); | |
| } | |
| if (!pathe.isAbsolute(normalizedPath) && !pathe.isAbsolute(cwd)) { | |
| throw new PathSecurityError( | |
| 'PATH_INVALID', | |
| path, | |
| normalizedPath, | |
| `Cannot resolve "${path}" against non-absolute cwd "${cwd}".`, | |
| ); | |
| } | |
| const abs = pathe.isAbsolute(normalizedPath) ? normalizedPath : pathe.resolve(cwd, normalizedPath); | |
| return pathe.normalize(abs); | |
| } | |
| export function isWithinDirectory( | |
| candidate: string, | |
| base: string, | |
| pathClass: PathClass = DEFAULT_PATH_CLASS, | |
| ): boolean { | |
| const nc = pathe.normalize(candidate); | |
| const nb = pathe.normalize(base); | |
| const comparableCandidate = pathClass === 'win32' ? nc.toLowerCase() : nc; | |
| const comparableBase = pathClass === 'win32' ? nb.toLowerCase() : nb; | |
| if (comparableCandidate === comparableBase) return true; | |
| const prefix = comparableBase.endsWith('/') ? comparableBase : comparableBase + '/'; | |
| return comparableCandidate.startsWith(prefix); | |
| } | |
| export function isWithinWorkspace( | |
| candidate: string, | |
| config: WorkspaceConfig, | |
| pathClass: PathClass = DEFAULT_PATH_CLASS, | |
| ): boolean { | |
| if (isWithinDirectory(candidate, config.workspaceDir, pathClass)) return true; | |
| for (const dir of config.additionalDirs) { | |
| if (isWithinDirectory(candidate, dir, pathClass)) return true; | |
| } | |
| return false; | |
| } | |
| export function extendWorkspaceWithSkillRoots<T extends WorkspaceConfig>( | |
| workspace: T, | |
| skillRoots: readonly string[], | |
| pathClass: PathClass = DEFAULT_PATH_CLASS, | |
| ): T { | |
| const additionalDirs = [...workspace.additionalDirs]; | |
| for (const root of skillRoots) { | |
| if (isWithinDirectory(root, workspace.workspaceDir, pathClass)) continue; | |
| if (additionalDirs.some((dir) => isWithinDirectory(root, dir, pathClass))) continue; | |
| additionalDirs.push(root); | |
| } | |
| if (additionalDirs.length === workspace.additionalDirs.length) return workspace; | |
| return { ...workspace, additionalDirs }; | |
| } | |
| export interface AssertPathOptions { | |
| readonly mode: PathAccessOperation; | |
| readonly checkSensitive?: boolean | undefined; | |
| readonly pathClass?: PathClass | undefined; | |
| } | |
| export interface ResolvePathAccessOptions { | |
| readonly operation: PathAccessOperation; | |
| readonly policy?: WorkspaceAccessPolicy | undefined; | |
| readonly pathClass?: PathClass | undefined; | |
| readonly homeDir?: string; | |
| readonly shellPathBridge?: ShellPathBridge; | |
| } | |
| export interface ResolvePathAccessPathOptions { | |
| readonly env: Pick< | |
| IHostEnvironment, | |
| 'pathClass' | 'homeDir' | 'osKind' | 'shellName' | 'shellPath' | |
| >; | |
| readonly workspace: WorkspaceConfig; | |
| readonly operation: PathAccessOperation; | |
| readonly policy?: WorkspaceAccessPolicy; | |
| readonly expandHome?: boolean; | |
| } | |
| function relativeOutsideMessage(path: string, operation: PathAccessOperation): string { | |
| const verb = | |
| operation === 'write' | |
| ? 'write or edit a file' | |
| : operation === 'search' | |
| ? 'search' | |
| : 'read a file'; | |
| return ( | |
| `"${path}" is not an absolute path. ` + | |
| `You must provide an absolute path to ${verb} outside the working directory.` | |
| ); | |
| } | |
| export function resolvePathAccess( | |
| path: string, | |
| cwd: string, | |
| config: WorkspaceConfig, | |
| options: ResolvePathAccessOptions, | |
| ): PathAccess { | |
| const pathClass = options.pathClass ?? DEFAULT_PATH_CLASS; | |
| const normalizedPath = | |
| options.shellPathBridge?.fromShellPath(path) ?? normalizeUserPath(path, pathClass); | |
| const expandedPath = expandUserPath(normalizedPath, options.homeDir, pathClass); | |
| const rawIsAbsolute = pathe.isAbsolute(expandedPath); | |
| const canonical = canonicalizePath(expandedPath, cwd, pathClass); | |
| const outsideWorkspace = !isWithinWorkspace(canonical, config, pathClass); | |
| const policy = options.policy ?? DEFAULT_WORKSPACE_ACCESS_POLICY; | |
| if (policy.checkSensitive && isSensitiveFile(canonical)) { | |
| throw new PathSecurityError( | |
| 'PATH_SENSITIVE', | |
| path, | |
| canonical, | |
| `"${path}" matches a sensitive-file pattern (env / credential / SSH key). ` + | |
| `Access is blocked to protect secrets.`, | |
| ); | |
| } | |
| if (outsideWorkspace) { | |
| switch (policy.guardMode) { | |
| case 'absolute-outside-allowed': | |
| if (!rawIsAbsolute) { | |
| throw new PathSecurityError( | |
| 'PATH_OUTSIDE_WORKSPACE', | |
| path, | |
| canonical, | |
| relativeOutsideMessage(path, options.operation), | |
| ); | |
| } | |
| break; | |
| case 'disabled': | |
| break; | |
| } | |
| } | |
| return { path: canonical, outsideWorkspace }; | |
| } | |
| export function resolvePathAccessPath( | |
| path: string, | |
| options: ResolvePathAccessPathOptions, | |
| ): string { | |
| const { env, workspace, operation, policy, expandHome = true } = options; | |
| return resolvePathAccess(path, workspace.workspaceDir, workspace, { | |
| operation, | |
| policy, | |
| pathClass: env.pathClass, | |
| homeDir: expandHome ? env.homeDir : undefined, | |
| shellPathBridge: env.pathClass === 'win32' ? getShellPathBridge(env) : undefined, | |
| }).path; | |
| } | |
| export function assertPathAllowed( | |
| path: string, | |
| cwd: string, | |
| config: WorkspaceConfig, | |
| options: AssertPathOptions, | |
| ): string { | |
| return resolvePathAccess(path, cwd, config, { | |
| operation: options.mode, | |
| pathClass: options.pathClass, | |
| policy: { | |
| guardMode: 'absolute-outside-allowed', | |
| checkSensitive: options.checkSensitive ?? DEFAULT_WORKSPACE_ACCESS_POLICY.checkSensitive, | |
| }, | |
| }).path; | |
| } | |