shellward 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -103,11 +103,12 @@ export function runProjectComplianceAudit(config, root) {
103
103
  const env = gatherEnvFacts();
104
104
  // 把文件中实测到的境外端点/依赖并入 facts(按 endpointId 或 provider 去重),
105
105
  // 使数据出境项基于真实证据(含 SDK 依赖通道)
106
- const seen = new Set(env.overseas.map(o => o.endpointId || o.provider_en));
106
+ const seen = new Set(env.overseas.map(o => (o.endpointId || o.provider_en || '').toLowerCase()));
107
107
  for (const f of scan.findings) {
108
108
  if (f.kind !== 'overseas')
109
109
  continue;
110
- const key = f.endpointId || f.provider_en || '';
110
+ // 按厂商去重(不区分大小写),避免"端点命中"与"SDK依赖命中"把同一厂商列两次
111
+ const key = (f.provider_en || f.endpointId || '').toLowerCase();
111
112
  if (!key || seen.has(key))
112
113
  continue;
113
114
  seen.add(key);
@@ -72,7 +72,7 @@ export function renderHtmlReport(report, scan, locale, meta) {
72
72
  </div>
73
73
  </section>`);
74
74
  // ===== 项目实测风险 =====
75
- S.push(sectionHead('🔍', t('项目实测风险', 'Project Scan Findings'), t(`已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''}`, `Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''}`)));
75
+ S.push(sectionHead('🔍', t('项目实测风险', 'Project Scan Findings'), t(`已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''} · 耗时 ${scan.durationMs ?? '?'}ms · 应用 ${scan.rulesChecked ?? '?'} 条检测规则`, `Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''} · ${scan.durationMs ?? '?'}ms · ${scan.rulesChecked ?? '?'} detection rules`)));
76
76
  if (scan.findings.length === 0) {
77
77
  S.push(`<div class="empty">🟢 ${t('未在项目文件中发现硬编码密钥、个人信息暴露或境外端点。', 'No hardcoded secrets, PII, or overseas endpoints found in project files.')}</div>`);
78
78
  }
@@ -18,6 +18,10 @@ export interface ProjectScanResult {
18
18
  truncated: boolean;
19
19
  findings: ProjectFinding[];
20
20
  counts: Record<FindingKind, number>;
21
+ /** 扫描耗时(ms) —— 透明度:让用户看到确实在干活 */
22
+ durationMs?: number;
23
+ /** 本次应用的检测规则总数(端点 + SDK 依赖 + 密钥/PII 模式) */
24
+ rulesChecked?: number;
21
25
  }
22
26
  /** 扫描项目目录,返回真实风险发现 */
23
27
  export declare function scanProject(root: string): ProjectScanResult;
Binary file
@@ -126,8 +126,8 @@ export function renderProjectFindings(scan, locale) {
126
126
  L.push(zh ? '## 🔍 项目实测风险' : '## 🔍 Project Scan Findings');
127
127
  L.push('');
128
128
  L.push(zh
129
- ? `> 已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限,部分未扫)' : ''}`
130
- : `> Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached, partial)' : ''}`);
129
+ ? `> 已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''} · 耗时 ${scan.durationMs ?? '?'}ms · 应用 ${scan.rulesChecked ?? '?'} 条检测规则`
130
+ : `> Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''} · ${scan.durationMs ?? '?'}ms · ${scan.rulesChecked ?? '?'} detection rules`);
131
131
  L.push('');
132
132
  if (scan.findings.length === 0) {
133
133
  L.push(zh ? '🟢 未在项目文件中发现硬编码密钥、个人信息暴露或境外端点。' : '🟢 No hardcoded secrets, PII exposure, or overseas endpoints found in project files.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellward",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "mcpName": "io.github.jnMetaCode/shellward",
5
5
  "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.",
6
6
  "keywords": [
@@ -171,10 +171,11 @@ export function runProjectComplianceAudit(config: ShellWardConfig, root: string)
171
171
 
172
172
  // 把文件中实测到的境外端点/依赖并入 facts(按 endpointId 或 provider 去重),
173
173
  // 使数据出境项基于真实证据(含 SDK 依赖通道)
174
- const seen = new Set(env.overseas.map(o => o.endpointId || o.provider_en))
174
+ const seen = new Set(env.overseas.map(o => (o.endpointId || o.provider_en || '').toLowerCase()))
175
175
  for (const f of scan.findings) {
176
176
  if (f.kind !== 'overseas') continue
177
- const key = f.endpointId || f.provider_en || ''
177
+ // 按厂商去重(不区分大小写),避免"端点命中"与"SDK依赖命中"把同一厂商列两次
178
+ const key = (f.provider_en || f.endpointId || '').toLowerCase()
178
179
  if (!key || seen.has(key)) continue
179
180
  seen.add(key)
180
181
  env.overseas.push({
@@ -96,8 +96,8 @@ export function renderHtmlReport(
96
96
 
97
97
  // ===== 项目实测风险 =====
98
98
  S.push(sectionHead('🔍', t('项目实测风险', 'Project Scan Findings'),
99
- t(`已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''}`,
100
- `Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''}`)))
99
+ t(`已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''} · 耗时 ${scan.durationMs ?? '?'}ms · 应用 ${scan.rulesChecked ?? '?'} 条检测规则`,
100
+ `Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''} · ${scan.durationMs ?? '?'}ms · ${scan.rulesChecked ?? '?'} detection rules`)))
101
101
 
102
102
  if (scan.findings.length === 0) {
103
103
  S.push(`<div class="empty">🟢 ${t('未在项目文件中发现硬编码密钥、个人信息暴露或境外端点。',
Binary file
@@ -141,8 +141,8 @@ export function renderProjectFindings(scan: ProjectScanResult, locale: 'zh' | 'e
141
141
  L.push(zh ? '## 🔍 项目实测风险' : '## 🔍 Project Scan Findings')
142
142
  L.push('')
143
143
  L.push(zh
144
- ? `> 已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限,部分未扫)' : ''}`
145
- : `> Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached, partial)' : ''}`)
144
+ ? `> 已扫描 ${scan.filesScanned} 个文件${scan.truncated ? '(已达上限)' : ''} · 耗时 ${scan.durationMs ?? '?'}ms · 应用 ${scan.rulesChecked ?? '?'} 条检测规则`
145
+ : `> Scanned ${scan.filesScanned} files${scan.truncated ? ' (limit reached)' : ''} · ${scan.durationMs ?? '?'}ms · ${scan.rulesChecked ?? '?'} detection rules`)
146
146
  L.push('')
147
147
 
148
148
  if (scan.findings.length === 0) {