feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import { promises as fs } from 'node:fs'
|
||||
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path'
|
||||
|
||||
export class SandboxPolicyError extends Error {}
|
||||
export function assertActive(signal?: AbortSignal): void {
|
||||
if (signal?.aborted) throw Object.assign(new Error('操作已取消'), { name: 'AbortError' })
|
||||
}
|
||||
export function within(root: string, target: string): boolean {
|
||||
const value = relative(root, target)
|
||||
return value !== '..' && !value.startsWith(`..${sep}`) && !isAbsolute(value)
|
||||
}
|
||||
const protectedNames = new Set(['.git', '.ssh', '.aws', '.azure', '.kube', '.gnupg', '.ycode', '.npmrc', '.pypirc', '.netrc', 'credentials', 'credentials.json', 'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519'])
|
||||
export function protectedPath(value: string): boolean {
|
||||
return value.split(/[\\/]/).some(part => {
|
||||
const name = part.toLowerCase()
|
||||
return protectedNames.has(name) || name.startsWith('.ycode-sandbox-') || name === '.env' || name.startsWith('.env.') || /\.(?:pem|pfx|p12|key)$/i.test(name)
|
||||
|| /[:\x00-\x1f]/.test(part) || /[. ]$/.test(part) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)
|
||||
})
|
||||
}
|
||||
/** Inspect each existing component. Node realpath alone does not identify hard links. */
|
||||
export async function resolveSandboxPath(rootInput: string, input: string, fullAccess = false): Promise<string> {
|
||||
if (typeof input !== 'string' || input.includes('\0')) throw new SandboxPolicyError('文件路径无效')
|
||||
const root = await fs.realpath(rootInput)
|
||||
const target = resolve(root, input.trim() || '.')
|
||||
if (!fullAccess && (!within(root, target) || protectedPath(relative(root, target)))) throw new SandboxPolicyError('默认权限不允许越界或访问受保护文件')
|
||||
if (fullAccess) {
|
||||
let existing = target
|
||||
const suffix: string[] = []
|
||||
for (;;) {
|
||||
try { return resolve(await fs.realpath(existing), ...suffix) }
|
||||
catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
||||
const parent = dirname(existing)
|
||||
if (parent === existing) throw error
|
||||
suffix.unshift(existing.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)))
|
||||
existing = parent
|
||||
}
|
||||
}
|
||||
}
|
||||
let existing = target
|
||||
const suffix: string[] = []
|
||||
for (;;) {
|
||||
const stat = await fs.lstat(existing).catch(error => {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null
|
||||
throw error
|
||||
})
|
||||
if (stat) {
|
||||
if (stat.isSymbolicLink() || stat.isFile() && stat.nlink > 1) throw new SandboxPolicyError('默认权限不允许符号链接、junction 或多重硬链接')
|
||||
const real = await fs.realpath(existing)
|
||||
if (!within(root, real)) throw new SandboxPolicyError('文件真实路径不在工作区内')
|
||||
const result = resolve(real, ...suffix)
|
||||
let ancestor = dirname(existing)
|
||||
while (within(root, ancestor)) {
|
||||
if ((await fs.lstat(ancestor)).isSymbolicLink()) throw new SandboxPolicyError('文件父目录包含链接')
|
||||
if (ancestor === root) break
|
||||
ancestor = dirname(ancestor)
|
||||
}
|
||||
return result
|
||||
}
|
||||
const parent = dirname(existing)
|
||||
if (parent === existing || !within(root, parent)) throw new SandboxPolicyError('无法解析工作区路径')
|
||||
suffix.unshift(existing.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)))
|
||||
existing = parent
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user