Install
$ agentstack add skill-nihao555-hub-claude-code-agent-skills-security-compliance-officer ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Security Compliance Officer
使用此技能当你需要进行安全合规系统设计、权限控制实现、PII 保护机制开发、或安全审计流程建立。
目标
产出完整的安全合规方案,包括权限分级系统、PII 保护机制、策略执行引擎、安全审计日志和合规检查清单。
核心能力
1. 权限模式系统 (src/utils/permissions/PermissionMode.ts)
Claude Code定义了 6 种权限模式:
export const PERMISSION_MODES = [
'default', // 标准审批流程
'plan', // 计划模式(只读)
'acceptEdits', // 自动接受编辑
'bypassPermissions', // 绕过权限(危险)
'dontAsk', // 不询问(全部允许)
'auto', // 自动模式(ant-only)
] as const
export const EXTERNAL_PERMISSION_MODES = [
'default',
'plan',
'acceptEdits',
'bypassPermissions',
'dontAsk',
] as const
export type PermissionMode = typeof PERMISSION_MODES[number]
export type ExternalPermissionMode = typeof EXTERNAL_PERMISSION_MODES[number]
interface PermissionModeConfig {
title: string
shortTitle: string
symbol: string
color: ModeColorKey
external: ExternalPermissionMode
}
const PERMISSION_MODE_CONFIG: Partial> = {
default: {
title: 'Default',
shortTitle: 'Default',
symbol: '',
color: 'text',
external: 'default',
},
plan: {
title: 'Plan Mode',
shortTitle: 'Plan',
symbol: '⏸️',
color: 'planMode',
external: 'plan',
},
acceptEdits: {
title: 'Accept edits',
shortTitle: 'Accept',
symbol: '⏩',
color: 'autoAccept',
external: 'acceptEdits',
},
bypassPermissions: {
title: 'Bypass Permissions',
shortTitle: 'Bypass',
symbol: '⏩',
color: 'error',
external: 'bypassPermissions',
},
dontAsk: {
title: "Don't Ask",
shortTitle: 'DontAsk',
symbol: '⏩',
color: 'error',
external: 'dontAsk',
},
...(feature('TRANSCRIPT_CLASSIFIER')
? {
auto: {
title: 'Auto mode',
shortTitle: 'Auto',
symbol: '⏩',
color: 'warning',
external: 'default',
},
}
: {}),
}
权限模式对比:
| 模式 | 文件读取 | 文件写入 | Shell 执行 | 适用场景 | |------|---------|---------|-----------|----------| | default | ✅ | ⚠️ 询问 | ⚠️ 询问 | 日常开发 | | plan | ✅ | ❌ | | 代码审查、学习 | | acceptEdits | ✅ | ✅ 自动 | ⚠️ 询问 | 信任的自动化任务 | | bypassPermissions | ✅ | ✅ | ✅ | 完全信任(危险!) | | dontAsk | ✅ | ✅ | ✅ | 无人值守任务 | | auto | ✅ | 🤖 AI 决定 | 🤖 AI 决定 | 高级用户(ant-only) |
2. 权限规则系统
interface PermissionRule {
id: string
pattern: string // Glob pattern for file paths
effect: 'allow' | 'deny'
tools?: string[] // Specific tools this rule applies to
reason?: string // Human-readable explanation
createdAt: number
updatedAt: number
}
/**
* Check if a tool can be used with current permissions
*/
export async function canUseTool(
toolName: string,
input: Record,
permissionContext: {
mode: PermissionMode
rules: PermissionRule[]
}
): Promise {
const { mode, rules } = permissionContext
// Bypass permissions mode - allow everything
if (mode === 'bypassPermissions') {
return { status: 'approved' }
}
// Don't Ask mode - allow everything
if (mode === 'dontAsk') {
return { status: 'approved' }
}
// Plan Mode - read/search/list only
if (mode === 'plan') {
const classification = classifyMcpToolForCollapse(toolName, '', {})
if (classification === 'read' || classification === 'search' || classification === 'list') {
return { status: 'approved' }
}
return {
status: 'denied',
reason: 'Plan mode only allows read/search/list operations'
}
}
// Default/Auto mode - check rules
for (const rule of rules) {
if (ruleMatches(rule, toolName, input)) {
if (rule.effect === 'allow') {
return { status: 'approved' }
} else {
return {
status: 'denied',
reason: rule.reason || 'Blocked by permission rule'
}
}
}
}
// No matching rule - ask user
return {
status: 'ask_user',
prompt: `Allow ${toolName} with arguments: ${JSON.stringify(input)}?`
}
}
/**
* Check if a rule matches the given tool and input
*/
function ruleMatches(
rule: PermissionRule,
toolName: string,
input: Record
): boolean {
// Check tool filter
if (rule.tools && !rule.tools.includes(toolName)) {
return false
}
// Check file path pattern
if (rule.pattern) {
const filePath = input.path as string | undefined
if (!filePath) return false
return picomatch.isMatch(filePath, rule.pattern)
}
return true
}
3. PII 保护系统
/**
* Types of PII to detect and redact
*/
type PIICategory =
| 'email' // Email addresses
| 'phone' // Phone numbers
| 'ssn' // Social Security Numbers
| 'credit_card' // Credit card numbers
| 'api_key' // API keys and tokens
| 'password' // Passwords
| 'home_path' // User home directory paths
| 'ip_address' // IP addresses
| 'aws_credentials' // AWS access keys
| 'github_token' // GitHub personal access tokens
/**
* PII detection patterns
*/
const PII_PATTERNS: Record = {
email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
phone: /\+?[1-9]\d{1,14}(?:\s|-|\.)/g,
ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
credit_card: /\b(?:\d{4}[- ]?){3}\d{4}\b/g,
api_key: /\b(sk-[a-zA-Z0-9]{32,})\b/g,
password: /(?:"password"|"passwd"|"pwd")\s*[:=]\s*"[^"]+"/gi,
home_path: new RegExp(`\\b/Users/[^/\\]+/|\$/HOME/|/home/[^/]+/`, 'g'),
ip_address: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
aws_credentials: /\b(AKIA[0-9A-Z]{16})\b/g,
github_token: /\b(ghp_[a-zA-Z0-9]{36})\b/g,
}
/**
* Redact PII from text
*/
export function redactPII(text: string, categories?: PIICategory[]): string {
const cats = categories || Object.keys(PII_PATTERNS) as PIICategory[]
let result = text
for (const category of cats) {
const pattern = PII_PATTERNS[category]
const replacement = `[REDACTED_${category.toUpperCase()}]`
result = result.replace(pattern, replacement)
}
return result
}
/**
* Sanitize object for logging (deep PII removal)
*/
export function sanitizeObjectForLogging(obj: unknown): unknown {
if (typeof obj === 'string') {
return redactPII(obj)
}
if (Array.isArray(obj)) {
return obj.map(item => sanitizeObjectForLogging(item))
}
if (obj !== null && typeof obj === 'object') {
const sanitized: Record = {}
for (const [key, value] of Object.entries(obj)) {
// Skip sensitive keys entirely
if (['password', 'secret', 'token', 'apiKey', 'credential'].includes(key.toLowerCase())) {
sanitized[key] = '[REDACTED_SENSITIVE_KEY]'
} else {
sanitized[key] = sanitizeObjectForLogging(value)
}
}
return sanitized
}
return obj
}
/**
* Log diagnostic information without PII
*/
export function logForDiagnosticsNoPII(
level: 'info' | 'warn' | 'error',
event: string,
metadata?: Record
): void {
const sanitized = sanitizeObjectForLogging(metadata || {})
const timestamp = new Date().toISOString()
const logFn = level === 'error' ? console.error :
level === 'warn' ? console.warn :
console.log
logFn(`[${timestamp}] [${level.toUpperCase()}] ${event}`, sanitized)
}
4. Denial Tracking System
interface DeniedAction {
id: string
timestamp: number
toolName: string
input: Record
reason: string
mode: PermissionMode
userId?: string
}
class DenialTracker {
private denials: DeniedAction[] = []
private readonly MAX_HISTORY = 1000
/**
* Track a denied action
*/
track(action: Omit): void {
const denial: DeniedAction = {
...action,
id: generateId('denial'),
timestamp: Date.now()
}
this.denials.push(denial)
// Keep only recent history
if (this.denials.length > this.MAX_HISTORY) {
this.denials.shift()
}
// Alert on suspicious patterns
this.detectSuspiciousPatterns()
}
/**
* Detect suspicious denial patterns
*/
private detectSuspiciousPatterns(): void {
const recentWindow = 5 * 60 * 1000 // 5 minutes
const now = Date.now()
const recentDenials = this.denials.filter(
d => now - d.timestamp 20) {
logForDiagnosticsNoPII('warn', 'suspicious_denial_pattern', {
count: recentDenials.length,
window_minutes: 5,
possible_cause: 'User may be stuck in denial loop'
})
}
// Same tool denied repeatedly
const toolCounts = new Map()
for (const denial of recentDenials) {
const count = toolCounts.get(denial.toolName) || 0
toolCounts.set(denial.toolName, count + 1)
}
for (const [tool, count] of toolCounts.entries()) {
if (count > 10) {
logForDiagnosticsNoPII('warn', 'repeated_tool_denial', {
tool,
count,
suggestion: 'Consider adjusting permission rules'
})
}
}
}
/**
* Get denial statistics
*/
getStatistics(period: number = 24 * 60 * 60 * 1000): {
total: number
byTool: Map
byReason: Map
trend: 'increasing' | 'stable' | 'decreasing'
} {
const cutoff = Date.now() - period
const recent = this.denials.filter(d => d.timestamp > cutoff)
const byTool = new Map()
const byReason = new Map()
for (const denial of recent) {
const toolCount = byTool.get(denial.toolName) || 0
byTool.set(denial.toolName, toolCount + 1)
const reasonCount = byReason.get(denial.reason) || 0
byReason.set(denial.reason, reasonCount + 1)
}
// Calculate trend (compare first half vs second half of period)
const midpoint = cutoff + period / 2
const firstHalf = recent.filter(d => d.timestamp d.timestamp >= midpoint).length
const trend = secondHalf > firstHalf * 1.2 ? 'increasing' :
secondHalf ` ${tool}: ${count}`)
.join('\n')}
By Reason:
${Array.from(stats.byReason.entries())
.map(([reason, count]) => ` ${reason}: ${count}`)
.join('\n')}
`
}
}
export const denialTracker = new DenialTracker()
5. 安全审计日志
interface AuditLogEntry {
id: string
timestamp: number
eventType: 'tool_use' | 'permission_change' | 'config_change' | 'auth_event'
actor: {
userId: string
sessionId: string
ipAddress?: string
}
action: string
resource?: string
outcome: 'success' | 'failure' | 'denied'
details?: Record
riskScore?: number // 0-100
}
class SecurityAuditor {
private logs: AuditLogEntry[] = []
private readonly RETENTION_DAYS = 90
/**
* Log a security-relevant event
*/
log(event: Omit): void {
const entry: AuditLogEntry = {
...event,
id: generateId('audit'),
timestamp: Date.now()
}
this.logs.push(entry)
// Auto-cleanup old entries
this.cleanupOldEntries()
// Alert on high-risk events
if (event.riskScore && event.riskScore > 80) {
this.alertHighRiskEvent(entry)
}
}
/**
* Query audit logs
*/
query(filters: {
startTime?: number
endTime?: number
eventType?: AuditLogEntry['eventType']
actor?: string
outcome?: AuditLogEntry['outcome']
minRiskScore?: number
}): AuditLogEntry[] {
return this.logs.filter(entry => {
if (filters.startTime && entry.timestamp filters.endTime) return false
if (filters.eventType && entry.eventType !== filters.eventType) return false
if (filters.actor && entry.actor.userId !== filters.actor) return false
if (filters.outcome && entry.outcome !== filters.outcome) return false
if (filters.minRiskScore && (entry.riskScore || 0) e.riskScore && e.riskScore > 50).length,
deniedActions: recentLogs.filter(e => e.outcome === 'denied').length,
authFailures: recentLogs.filter(e => e.eventType === 'auth_event' && e.outcome === 'failure').length
},
recommendations: this.generateRecommendations(recentLogs, standard)
}
return JSON.stringify(report, null, 2)
}
private cleanupOldEntries(): void {
const cutoff = Date.now() - (this.RETENTION_DAYS * 24 * 60 * 60 * 1000)
this.logs = this.logs.filter(entry => entry.timestamp > cutoff)
}
private alertHighRiskEvent(entry: AuditLogEntry): void {
console.error('[SECURITY ALERT] High-risk event detected:', {
eventId: entry.id,
type: entry.eventType,
action: entry.action,
riskScore: entry.riskScore,
timestamp: new Date(entry.timestamp).toISOString()
})
}
private generateRecommendations(
logs: AuditLogEntry[],
standard: string
): string[] {
const recommendations: string[] = []
// Check for common security issues
const failedAuths = logs.filter(e => e.eventType === 'auth_event' && e.outcome === 'failure').length
if (failedAuths > 10) {
recommendations.push('Multiple authentication failures detected - consider implementing account lockout')
}
const deniedActions = logs.filter(e => e.outcome === 'denied').length
if (deniedActions > logs.length * 0.3) {
recommendations.push('High denial rate - review permission policies for usability')
}
return recommendations
}
}
export const securityAuditor = new SecurityAuditor()
6. 安全检查清单
## 权限控制检查
- [ ] 所有敏感操作都有权限检查
- [ ] 权限规则可配置且易于理解
- [ ] 默认拒绝未知操作
- [ ] 权限变更有审计日志
- [ ] 支持最小权限原则
## PII 保护检查
- [ ] 所有日志都经过 PII 过滤
- [ ] 敏感数据加密存储
- [ ] API 密钥不硬编码
- [ ] 用户路径不泄露
- [ ] 错误信息不包含敏感数据
## 审计合规检查
- [ ] 所有安全事件都有日志
- [ ] 日志保留期符合法规要求
- [ ] 支持日志查询和导出
- [ ] 异常行为自动告警
- [ ] 定期生成合规报告
## 认证授权检查
- [ ] OAuth token 安全存储
- [ ] Token 过期自动刷新
- [ ] 支持多因素认证
- [ ] Session 超时合理设置
- [ ] 注销后彻底清理凭证
7. 安全事件响应流程
## 安全事件分级
### P0 - 严重 (Critical)
- 凭证泄露
- 未授权访问生产数据
- 大规模 PII 泄露
**响应时间**: 立即 ( 99%
- 脱敏完整性
- 零 PII 泄露到日志
审计日志 (0-10分):
- 关键操作全记录
- 日志保留期合规
- 查询功能完善
Denial Tracking (0-10分):
- 拒绝操作全记录
- 可疑模式检测
- 统计报告准确
事件响应 (0-10分):
- 事件分级明确
- 响应时间达标
- 升级路径清晰
总分评级:
- 90-100: Excellent (生产就绪)
- 75-89: Good (少量改进)
- 60-74: Fair (需要加强)
- {...})
test('plan mode blocks writes', () => {...})
test('rules are enforced', () => {...})
# 2. PII 保护测试
test('detects emails', () => {...})
test('redacts home paths', () => {...})
test('filters API keys', () => {...})
# 3. 审计日志测试
test('logs tool usage', () => {...})
test('retains logs for 90 days', () => {...})
# 4. Denial tracking 测试
test('tracks denied actions', () => {...})
test('detects suspicious patterns', () => {...})
# 5. 事件响应测试
test('classifies events correctly', () => {...})
test('alerts on high-risk events', () => {...})
AI IDE 常见陷阱检测
🔴 高危问题(必须修复)
// ❌ 权限绕过
if (user.isAdmin) {
return { status: 'approved' } // 跳过正常权限检查!
}
// AI IDE 应该:立即警告并阻止
// ❌ PII 明文日志
console.log(`User ${email} performed ${action}`) // email 未脱敏!
// AI IDE 应该:建议使用 logForDiagnosticsNoPII
// ❌ 硬编码凭证
const API_KEY = 'sk-xxx...' // 直接写在代码里!
// AI IDE 应该:建议使用环境变量或密钥管理服务
// ❌ 无审计日志
await sensitiveOperation() // 没有记录到审计日志
// AI IDE 应该:建议添加 securityAuditor.log()
🟡 中等风险(建议优化)
// ⚠️ 权限规则过于宽松
const rules = [{ pattern: '*', effect: 'allow' }] // 允许所有!
// AI IDE 应该:建议更精细的规则
// ⚠️ PII 检测不完整
const patterns = { email: /.../ } // 只有 email,缺少其他类型
// AI IDE 应该:建议补充完整的 PII 类型列表
🟢 低风险(可选改进)
// 💡 可以优化的模式
const isAllowed = user.role === 'admin' // 简单的 RBAC
// AI IDE 可以建议:实现基于属性的访问控制 (ABAC)
AI IDE 代码审查检查清单
在 PR/MR阶段,AI IDE应该自动检查:
权限控制
- [ ] 所有敏感操作有 canUseTool 检查
- [ ] 权限模式正确处理
- [ ] 规则匹配引擎完整
- [ ] 用户询问界面友好
PII 保护
- [ ] 使用 sanitizeObjectForLogging
- [ ] 所有日志通过 logForDiagnosticsNoPII
- [ ] PII 模式检测完整
- [ ] 无硬编码凭证
审计日志
- [ ] 关键操作记录完整
- [ ] 事件类型正确分类
- [ ] 参与者信息准确
- [ ] 结果状态明确
Denial Tracking
- [ ] 拒绝操作全部记录
- [ ] 可疑模式检测启用
- [ ] 统计数据准确
- [ ] 报告导出可用
事件响应
- [ ] 事件分级明确(P0-P3)
- [ ] 响应时间定义
- [ ] 升级路径清晰
- [ ] 高风崄事件告警
AI IDE 集成实现示例
interface SecurityRule {
id: string
description: s
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [nihao555-hub](https://github.com/nihao555-hub)
- **Source:** [nihao555-hub/claude-code-agent-skills](https://github.com/nihao555-hub/claude-code-agent-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.