这是「GSD 全景代码解析」专题的第 51 篇。
一、安全威胁模型
AI Agent 工具链面临独特的安全挑战:
graph TD
A[安全威胁] --> B[数据泄露]
A --> C[凭据暴露]
A --> D[注入攻击]
A --> E[权限提升]
B --> B1[敏感信息写入代码]
B --> B2[日志泄露]
C --> C1[API Key 硬编码]
C --> C2[环境变量泄露]
D --> D1[提示词注入]
D --> D2[间接提示词注入]
E --> E1[越权文件访问]
E --> E2[危险命令执行]二、Secret Audit 系统
secret-audit 是 GSD 的第一道安全防线,负责检测代码中可能泄露的敏感信息。
2.1 检测规则引擎
interface SecretRule {
name: string;
pattern: RegExp;
severity: 'critical' | 'high' | 'medium' | 'low';
category: 'api_key' | 'password' | 'token' | 'private_key';
}
const SECRET_RULES: SecretRule[] = [
{
name: 'OpenAI API Key',
pattern: /sk-[a-zA-Z0-9]{48}/,
severity: 'critical',
category: 'api_key'
},
{
name: 'AWS Access Key',
pattern: /AKIA[0-9A-Z]{16}/,
severity: 'critical',
category: 'api_key'
},
{
name: 'Generic Secret',
pattern: /(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/i,
severity: 'high',
category: 'password'
},
{
name: 'Private Key',
pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
severity: 'critical',
category: 'private_key'
}
];2.2 审计流程
class SecretAuditor {
async audit(projectPath: string): Promise<AuditResult> {
const findings: Finding[] = [];
const files = await this.getAuditFiles(projectPath);
for (const file of files) {
const content = await fs.readFile(file, 'utf-8');
for (const rule of SECRET_RULES) {
const matches = content.match(rule.pattern);
if (matches) {
findings.push({
file,
rule: rule.name,
severity: rule.severity,
line: this.getLineNumber(content, matches[0]),
snippet: this.maskSecret(matches[0])
});
}
}
}
return { findings, passed: findings.length === 0 };
}
private maskSecret(secret: string): string {
if (secret.length <= 8) return '****';
return secret.slice(0, 4) + '****' + secret.slice(-4);
}
}2.3 审计报告示例
═══════════════════════════════════════════════
GSD Secret Audit Report
═══════════════════════════════════════════════
扫描文件: 127
发现问题: 3
[CRITICAL] OpenAI API Key
文件: src/config.ts:15
内容: sk-****1234
建议: 使用环境变量或密钥管理服务
[HIGH] Generic Secret
文件: .env:3
内容: PASSWORD=****
建议: 将 .env 添加到 .gitignore
[CRITICAL] Private Key
文件: deploy/key.pem:1
内容: -----BEGIN PRIVATE KEY-----
建议: 从仓库中移除,使用 CI/CD 密钥注入
═══════════════════════════════════════════════三、Credential Gate 凭据门控
credential-gate 确保只有经过授权的 Agent 才能访问敏感操作。
3.1 权限矩阵
| Agent | 读取代码 | 写入代码 | 执行命令 | 访问密钥 |
|---|---|---|---|---|
| Planner | ✓ | ✗ | ✗ | ✗ |
| Executor | ✓ | ✓ | △* | ✗ |
| Debugger | ✓ | ✓ | △* | ✗ |
| Auditor | ✓ | ✗ | ✗ | ✗ |
| Deployer | ✓ | ✗ | ✓ | ✓ |
*△ 表示需要显式确认的危险操作
3.2 命令白名单机制
const COMMAND_ALLOWLIST: Record<string, string[]> = {
'Executor': ['npm test', 'npm run build', 'git status'],
'Debugger': ['npm test', 'node --inspect'],
'Deployer': ['npm run deploy', 'docker build', 'docker push']
};
class CredentialGate {
async validateCommand(agent: string, command: string): Promise<boolean> {
const allowed = COMMAND_ALLOWLIST[agent] || [];
// 精确匹配
if (allowed.includes(command)) return true;
// 前缀匹配(允许参数)
const baseCommand = command.split(' ')[0];
if (allowed.some(a => a.startsWith(baseCommand))) {
// 需要额外审查参数
return await this.reviewArguments(agent, command);
}
return false;
}
private async reviewArguments(agent: string, command: string): Promise<boolean> {
// 检查危险参数
const dangerousFlags = ['-rf', '--force', '> /dev/null', '| sh'];
if (dangerousFlags.some(f => command.includes(f))) {
logger.warn(`Dangerous command blocked: ${command}`);
return false;
}
return true;
}
}3.3 沙箱执行
class SandboxExecutor {
async execute(command: string, options: SandboxOptions): Promise<ExecResult> {
const sandbox = await this.createSandbox({
readonlyPaths: [options.projectRoot],
writablePaths: [options.tempDir],
network: options.allowNetwork ? 'limited' : 'none',
env: this.sanitizeEnv(process.env)
});
try {
return await sandbox.run(command, { timeout: options.timeout || 30000 });
} finally {
await sandbox.destroy();
}
}
private sanitizeEnv(env: NodeJS.ProcessEnv): Record<string, string> {
// 移除敏感环境变量
const sensitive = ['OPENAI_API_KEY', 'AWS_SECRET_KEY', 'GITHUB_TOKEN'];
const clean = { ...env };
for (const key of sensitive) {
delete clean[key];
}
return clean;
}
}四、提示词注入防御
4.1 输入净化
class PromptSanitizer {
sanitize(userInput: string): string {
// 1. 移除控制字符
let cleaned = userInput.replace(/[\x00-\x08\x0b-\x0c\x0e-\x1f]/g, '');
// 2. 转义特殊标记
cleaned = cleaned
.replace(/\{\{/g, '\\{\\{')
.replace(/\}\}/g, '\\}\\}');
// 3. 检测越狱尝试
const jailbreakPatterns = [
/ignore previous instructions/i,
/you are now \w+/i,
/DAN|Do Anything Now/i,
/system:?\s*prompt/i
];
for (const pattern of jailbreakPatterns) {
if (pattern.test(cleaned)) {
throw new PromptInjectionDetected(pattern.source);
}
}
return cleaned;
}
}4.2 结构化输出约束
interface SafePrompt {
system: string; // 固定系统提示(不可变)
context: string; // 项目上下文
instruction: string; // 任务指令
userInput: string; // 已净化的用户输入
separator: string; // 明确的分隔符
}
function buildSafePrompt(parts: SafePrompt): string {
return `${parts.system}
${parts.separator}
CONTEXT:
${parts.context}
${parts.separator}
INSTRUCTION:
${parts.instruction}
${parts.separator}
USER INPUT:
${parts.userInput}
${parts.separator}
IMPORTANT: Only respond within the scope of the instruction above.`;
}五、安全事件响应
graph LR
A[检测到安全事件] --> B{严重程度?}
B -->|Critical| C[立即中止执行]
B -->|High| D[阻断操作并告警]
B -->|Medium| E[记录日志并提示]
B -->|Low| F[仅记录日志]
C --> G[通知管理员]
D --> G
E --> H[继续执行]
F --> H六、安全最佳实践
| 实践 | 实施方式 | 优先级 |
|---|---|---|
| 密钥外置 | 环境变量 + 密钥管理服务 | P0 |
| 最小权限 | Agent 权限矩阵 | P0 |
| 输入验证 | PromptSanitizer | P0 |
| 审计日志 | 所有敏感操作记录 | P1 |
| 定期扫描 | CI 中的 secret-audit | P1 |
| 依赖审计 | npm audit + Snyk | P1 |
| 沙箱执行 | 隔离运行环境 | P2 |
下一篇预告: 第 52 篇《漂移检测与 Schema 验证:配置一致性、文档同步与版本兼容性》
我们将深入解析 GSD 如何确保配置、代码与文档三者的一致性,以及 Schema 验证在版本升级中的关键作用。敬请期待。