Skip to content

Commit a810d2e

Browse files
committed
feat: 数据出境改"需评估"(不武断判违规) + 报告内置接入运行时指引 v0.7.13
- 境外大模型调用: 控制项 fail→warn,端点severity critical→high/依赖high→medium,标签改"需评估出境" (检出是事实,是否违规取决于发什么数据;国内不发敏感数据的项目不再0分D) - 报告加"接入运行时"段:可复制MCP配置,把"待核验需接入"变成一键可操作
1 parent d674889 commit a810d2e

7 files changed

Lines changed: 37 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8+
## [0.7.13] - 2026-06-21
9+
10+
### Changed — 数据出境改为「需评估」+ 报告内置「接入运行时」指引
11+
- **境外大模型调用不再武断判「不合规」**:检出"用了 OpenAI/Anthropic"是事实,是否违规取决于是否发送个人信息/重要数据——降为 **🟡 需评估**(控制项 fail→warn,端点 severity critical→high、依赖 high→medium),分类标签改「境外大模型调用(需评估出境)」。国内很多不发敏感数据的项目不再动辄 0 分 D
12+
- **报告内置「接入运行时」指引**:⚪ 待核验项需 ShellWard 运行时才能验证——报告里直接给**可复制的 MCP 配置**(贴到 Claude Desktop/Cursor 即接入),把"需要接入"变成一键可操作
13+
814
## [0.7.12] - 2026-06-21
915

1016
### Fixed — 上传报告"返回"失效

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "shellward",
3-
"version": "0.7.12",
3+
"version": "0.7.13",
44
"mcpName": "io.github.jnMetaCode/shellward",
55
"description": "AI agent security & MCP security middleware — prompt injection detection, AI firewall, runtime guardrails & data-loss prevention for LLM tool calls. 8-layer defense against data exfiltration & dangerous commands. Zero dependencies. SDK + OpenClaw plugin. Supports LangChain, AutoGPT, Claude Code, Cursor, OpenAI Agents, Hermes Agent.",
66
"keywords": [

src/compliance/audit.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,12 @@ function checkEnv(c: ComplianceControl, env: EnvFacts): ControlResult {
317317
if (env.overseas.length > 0) {
318318
const names = env.overseas.map(o => o.provider_zh).join(', ')
319319
const namesEn = env.overseas.map(o => o.provider_en).join(', ')
320-
return mk(c, 'fail',
321-
`检测到境外大模型端点配置: ${names} — 若向其发送个人信息/重要数据即构成数据出境,须走合规路径`,
322-
`Overseas LLM endpoint(s) configured: ${namesEn} — sending PI/important data = cross-border export`)
320+
// 检出境外调用是"事实",是否违规取决于是否发送 PII/重要数据 → 标"需评估"而非"不合规"
321+
return mk(c, 'warn',
322+
`检测到境外大模型调用: ${names}。是否违规取决于发送的数据:涉及个人信息/重要数据需走合规路径或改用境内模型;仅非个人/非重要数据通常可接受。`,
323+
`Overseas LLM detected: ${namesEn}. Compliance depends on the data sent — PI/important data needs a compliant path; non-personal data is usually fine.`)
323324
}
324-
return mk(c, 'pass', '未检测到境外大模型端点配置', 'No overseas LLM endpoint detected')
325+
return mk(c, 'pass', '未检测到境外大模型调用', 'No overseas LLM detected')
325326
}
326327
return mk(c, 'manual', '需人工确认', 'Manual check required')
327328
}

src/compliance/html-report.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const STATUS: Record<ControlStatus, { zh: string; en: string; cls: string }> = {
2020
}
2121

2222
const KIND: Record<FindingKind, { zh: string; en: string; icon: string }> = {
23-
overseas: { zh: '数据出境风险', en: 'Data export risk', icon: '🌐' },
23+
overseas: { zh: '境外大模型调用(需评估出境)', en: 'Overseas LLM (assess export)', icon: '🌐' },
2424
secret: { zh: '硬编码密钥', en: 'Hardcoded secret', icon: '🔑' },
2525
pii: { zh: '个人信息暴露', en: 'PII exposure', icon: '🪪' },
2626
'env-perm': { zh: '.env 权限', en: '.env permission', icon: '📂' },
@@ -192,6 +192,22 @@ export function renderHtmlReport(
192192
S.push('</tbody></table></div>')
193193
}
194194

195+
// ===== 接入运行时指引:把 ⚪ 待核验项变成可验证(回答"需要接入才能测")=====
196+
if (report.staticScan && report.manual > 0) {
197+
const cfg = `{
198+
"mcpServers": {
199+
"shellward": { "command": "npx", "args": ["-y", "-p", "shellward", "shellward-mcp"] }
200+
}
201+
}`
202+
S.push(sectionHead('🔌', t('让待核验项可验证:接入运行时', 'Make ⚪ items verifiable: deploy runtime'),
203+
t('静态扫描测不了运行时行为,接入后这些项才能被持续验证', 'Static scan cannot test runtime behavior; deploy to validate')))
204+
S.push(`<p class="note">${t(
205+
`把下面这段加到 Claude Desktop / Cursor / OpenClaw 的 MCP 配置,重启后 ShellWard 就作为<b>运行时防护</b>在你的 AI 应用里运行——上方 ${report.manual} 项(审计留存、运行时拦截、数据外发管控等)即可被真实验证。`,
206+
`Add this to your Claude Desktop / Cursor / OpenClaw MCP config and restart — ShellWard then runs as a <b>runtime guard</b>, validating the ${report.manual} ⚪ items above.`)}</p>`)
207+
S.push(`<div class="cfg"><pre id="mcpcfg">${esc(cfg)}</pre><button onclick="(function(b){navigator.clipboard&&navigator.clipboard.writeText(document.getElementById('mcpcfg').textContent).then(function(){b.textContent='${t('已复制', 'Copied')}'})})(this)">${t('复制', 'Copy')}</button></div>`)
208+
S.push(`<p class="muted">${t('或命令行直接起:', 'Or run directly:')} <code>npx shellward mcp</code> · ${t('文档', 'Docs')}: <a href="https://github.com/jnMetaCode/shellward">github.com/jnMetaCode/shellward</a></p>`)
209+
}
210+
195211
const disclaimer = t(
196212
'本报告由 ShellWard 合规网关自动生成,帮助评估并满足合规技术要求,不构成法律意见,亦不替代算法备案/定级备案/PIA 等主体责任。⚪ 待确认项需结合业务人工判定。',
197213
'Generated by ShellWard Compliance Gateway. Assists with technical compliance; not legal advice. ⚪ items require manual review.')
@@ -364,6 +380,12 @@ table.tbl td.right{width:64px}
364380
.manual-note{margin:8px 36px 12px;font-size:13px;color:#475569;background:#eff6ff;
365381
border-left:3px solid #3b82f6;line-height:1.6}
366382
.manual-note code{background:#dbeafe;padding:1px 6px;border-radius:5px}
383+
.cfg{position:relative;margin:8px 36px}
384+
.cfg pre{background:#0f172a;color:#e2e8f0;border-radius:10px;padding:16px 18px;margin:0;
385+
font-family:ui-monospace,Menlo,monospace;font-size:12.5px;overflow-x:auto;line-height:1.5}
386+
.cfg button{position:absolute;top:10px;right:12px;background:#1e293b;color:#94a3b8;border:0;
387+
border-radius:6px;padding:5px 12px;font-size:12px;cursor:pointer}
388+
.cfg button:hover{color:#fff}
367389
368390
/* 法规分组 */
369391
.reg{margin:14px 36px;padding:0;border:1px solid var(--line);border-radius:12px;overflow:hidden}

src/compliance/project-scan.ts

98 Bytes
Binary file not shown.

src/compliance/report.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export function renderComplianceReport(report: ComplianceReport, locale: 'zh' |
130130
}
131131

132132
const KIND_LABEL: Record<FindingKind, { zh: string; en: string; icon: string }> = {
133-
overseas: { zh: '数据出境风险', en: 'Data export risk', icon: '🌐' },
133+
overseas: { zh: '境外大模型调用(需评估出境)', en: 'Overseas LLM (assess export)', icon: '🌐' },
134134
secret: { zh: '硬编码密钥', en: 'Hardcoded secret', icon: '🔑' },
135135
pii: { zh: '个人信息暴露', en: 'PII exposure', icon: '🪪' },
136136
'env-perm': { zh: '.env 权限', en: '.env permission', icon: '📂' },

test-compliance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const badFacts: EnvFacts = {
7878
const rootFail = bad.results.find(r => r.control.id === 'mlps-not-root')
7979
test('root 运行 → mlps-not-root = fail', rootFail?.status === 'fail')
8080
const exportFail = bad.results.find(r => r.control.id === 'cbdt-overseas-llm')
81-
test('境外端点 → cbdt-overseas-llm = fail', exportFail?.status === 'fail')
81+
test('境外端点 → cbdt-overseas-llm = warn(需评估,不武断判违规)', exportFail?.status === 'warn')
8282
const auditFail = bad.results.find(r => r.control.id === 'csl-audit-log')
8383
test('无审计日志 → csl-audit-log = fail', auditFail?.status === 'fail')
8484
test('坏环境得分 < 干净环境得分', bad.score < good.score, `${bad.score} vs ${good.score}`)

0 commit comments

Comments
 (0)