Skip to content

Commit 4c61775

Browse files
committed
feat(web): 恢复上传文件夹为首选 + 报告返回栏 v0.7.10
- 上传文件夹设为首选(最方便),目录浏览器收折叠作零上传备选 - 报告页加「←返回/再扫一个」顶栏(web),扫完能回首页继续 - HtmlReportMeta 增 backLink
1 parent 53aa965 commit 4c61775

4 files changed

Lines changed: 45 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ 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.10] - 2026-06-21
9+
10+
### Changed — web 客户端更易操作
11+
- **恢复「上传文件夹」为首选**(一次选定最方便),目录浏览器(零上传)收进折叠项作备选;上传仍自动跳过 node_modules,并说明"提示上传 N 个文件"是浏览器行为、实际只发源码/配置且不出本机
12+
- **报告页加「← 返回 / 再扫一个」顶栏**(web 场景)——扫完能回首页继续扫下一个,不再是死胡同
13+
- 报告 `HtmlReportMeta``backLink`(CLI 导出不受影响)
14+
815
## [0.7.9] - 2026-06-20
916

1017
### Changed — 「待确认」不再啰嗦/吓人

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.9",
3+
"version": "0.7.10",
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/html-report.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const SEV: Record<string, { zh: string; en: string }> = {
4343
export interface HtmlReportMeta {
4444
/** 扫描的项目根 */
4545
root: string
46+
/** 可选:web 场景下的"返回/再扫一个"链接(CLI 导出时不传) */
47+
backLink?: string
4648
}
4749

4850
/** 生成自包含 HTML 合规报告 */
@@ -203,6 +205,7 @@ export function renderHtmlReport(
203205
<style>${CSS}</style>
204206
</head>
205207
<body>
208+
${meta.backLink ? `<div class="backbar"><a href="${esc(meta.backLink)}">← ${t('返回 / 再扫一个', 'Back / scan another')}</a></div>` : ''}
206209
<main>
207210
<header>
208211
<div class="brand">🛡️ Shell<span>Ward</span> <em>${t('合规网关', 'Compliance Gateway')}</em></div>
@@ -263,6 +266,10 @@ const CSS = `
263266
body{margin:0;background:var(--bg);color:var(--ink);
264267
font:15px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
265268
-webkit-font-smoothing:antialiased}
269+
.backbar{position:sticky;top:0;z-index:9;background:#0f172a;padding:10px 24px}
270+
.backbar a{color:#fff;font-weight:600;font-size:14px;text-decoration:none}
271+
.backbar a:hover{color:#fca5a5}
272+
@media print{.backbar{display:none}}
266273
main{max-width:880px;margin:28px auto;background:var(--card);border-radius:16px;
267274
box-shadow:0 1px 3px rgba(15,23,42,.06),0 12px 32px rgba(15,23,42,.07);overflow:hidden}
268275
header{padding:30px 36px 22px;background:linear-gradient(180deg,#fafbfd,#fff);border-bottom:1px solid var(--line)}

src/web/scan-server.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async function handleRepo(res: any, repo: string, locale: 'zh' | 'en', inc: () =
113113
try {
114114
await cloneRepo(v.url, dir)
115115
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
116-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: v.url }))
116+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: v.url, backLink: '/' }))
117117
} catch (e: any) {
118118
const msg = esc(e?.message || String(e))
119119
send(res, 502, 'text/html', errorPage(
@@ -133,7 +133,7 @@ async function handleLocal(res: any, path: string, locale: 'zh' | 'en', inc: ()
133133
inc()
134134
try {
135135
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, root)
136-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root }))
136+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root, backLink: '/' }))
137137
} finally {
138138
dec()
139139
}
@@ -176,7 +176,7 @@ async function handleUpload(req: any, res: any, locale: 'zh' | 'en', inc: () =>
176176
}
177177
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
178178
const rootName = typeof payload.root === 'string' && payload.root ? payload.root : '(uploaded folder)'
179-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: rootName }))
179+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: rootName, backLink: '/' }))
180180
} catch (e: any) {
181181
send(res, 500, 'text/html', errorPage('扫描失败:' + esc(e?.message || String(e))))
182182
} finally {
@@ -226,7 +226,7 @@ function handleDemo(res: any, locale: 'zh' | 'en', inc: () => void, dec: () => v
226226
writeFileSync(join(dir, '.env'),
227227
'AWS_ACCESS_KEY=AKIARZ9MKP2QWLS7YV3N\nDB_PASSWORD=Sup3rS3cretProdPwd2026\n')
228228
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
229-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: '示例项目(含风险)/ demo-ai-app' }))
229+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: '示例项目(含风险)/ demo-ai-app', backLink: '/' }))
230230
} catch (e: any) {
231231
send(res, 500, 'text/html', errorPage('演示失败:' + esc(e?.message || String(e))))
232232
} finally {
@@ -261,34 +261,44 @@ function send(res: any, code: number, type: string, body: string) {
261261
function formPage(local: boolean): string {
262262
const urlForm = `
263263
<form action="/scan" method="get" onsubmit="var b=this.querySelector('button');b.disabled=true;b.textContent='扫描中…(大仓库需 10–60 秒,请勿重复点击)';">
264-
<label>${local ? ' ' : ''}公开仓库地址</label>
264+
<label>${local ? ' ' : ''}公开仓库地址</label>
265265
<input name="repo" placeholder="https://github.com/owner/repo"${local ? '' : ' autofocus'}>
266266
<button type="submit">${local ? '体检该仓库 →' : '开始体检 →'}</button>
267-
<p class="hint">仅支持公开仓库(GitHub / GitLab / Gitee / Bitbucket)。大仓库可能超时——${local ? '此时改用上方「选择文件夹」更稳。' : '<b>大仓库 / 私有代码请用本地客户端或 CLI</b>:<code>npx shellward web --local</code> / <code>npx shellward scan</code>(不上传)。'}</p>
267+
<p class="hint">仅支持公开仓库(GitHub / GitLab / Gitee / Bitbucket)。大仓库可能超时——${local ? '此时用上方「上传文件夹」更稳。' : '<b>大仓库 / 私有代码请用本地客户端或 CLI</b>:<code>npx shellward web --local</code> / <code>npx shellward scan</code>(不上传)。'}</p>
268268
</form>`
269269

270-
const uploadForm = local ? `
271-
<label>① 在本机点选项目文件夹(推荐 · 零上传)</label>
272-
<div class="browser">
273-
<div class="bpath" id="curpath">加载中…</div>
274-
<ul class="dirs" id="dirs"></ul>
275-
</div>
276-
<button id="scanbtn" type="button">✅ 扫描当前文件夹 →</button>
277-
<p class="hint">📂 在你电脑上点进项目目录,再点"扫描当前文件夹"。<b>服务端直接读取本机文件、零上传、不出本机</b>,自动跳过 node_modules,无需选 3 万个文件。</p>
270+
// 本地模式:① 上传文件夹(一次选定,最方便) ② 点选文件夹(零上传) ③ URL
271+
const localForms = local ? `
272+
<form id="dirform">
273+
<label>① 上传项目文件夹(最方便)</label>
274+
<input type="file" id="dir" webkitdirectory directory multiple>
275+
<button id="dbtn" type="submit">开始体检 →</button>
276+
<div id="status" class="status"></div>
277+
<p class="hint">选你的项目文件夹即可。<b>浏览器可能提示"上传 N 个文件"——放心:实际只发送源码/配置(自动跳过 node_modules、图片、超大文件),且只到<u>本机的本地服务</u>,不出本机。</b></p>
278+
</form>
279+
<details class="alt"><summary>不想上传?点选文件夹(零上传)</summary>
280+
<label>② 在本机点选项目文件夹(零上传)</label>
281+
<div class="browser">
282+
<div class="bpath" id="curpath">加载中…</div>
283+
<ul class="dirs" id="dirs"></ul>
284+
</div>
285+
<button id="scanbtn" type="button">✅ 扫描当前文件夹 →</button>
286+
<p class="hint">服务端直接读取本机文件、零上传、不出本机,自动跳过 node_modules。</p>
287+
</details>
278288
<div class="or">— 或 —</div>` : ''
279289

280290
return page('ShellWard 合规体检', `
281291
<div class="hero">
282292
<div class="logo">🛡️ Shell<span>Ward</span> 合规网关</div>
283293
<h1>AI 应用合规体检</h1>
284-
<p class="sub">${local ? '选项目文件夹或贴公开仓库链接' : '贴公开仓库链接'},30 秒查出数据出境 / 硬编码密钥 / 个人信息暴露等中国合规红线。</p>
285-
${uploadForm}
294+
<p class="sub">${local ? '上传/点选项目文件夹,或贴公开仓库链接' : '贴公开仓库链接'},30 秒查出数据出境 / 硬编码密钥 / 个人信息暴露等中国合规红线。</p>
295+
${localForms}
286296
${urlForm}
287297
<p class="demo">🤔 觉得"秒出"不真? <a href="/demo">▶ 看一个含风险的示例报告</a>(同样秒出,但满屏发现 + 行号)</p>
288298
<p class="foot">网安法 2026 · PIPL · 等保2.0 · 数据出境 · AI标识 | 零依赖 · 开源 ·
289299
<a href="https://github.com/jnMetaCode/shellward">GitHub ⭐</a></p>
290300
</div>
291-
${local ? BROWSE_SCRIPT : ''}`)
301+
${local ? UPLOAD_SCRIPT + BROWSE_SCRIPT : ''}`)
292302
}
293303

294304
// 本地目录浏览器:点选文件夹 → 服务端直接扫(零上传,不读 node_modules)
@@ -382,6 +392,9 @@ form{margin:0 0 14px}.or{text-align:center;color:#94a3b8;font-size:13px;margin:6
382392
.status{display:none;margin:10px 0 0;padding:10px 14px;border-radius:8px;background:#f1f5f9;
383393
color:#334155;font-size:13.5px;border-left:3px solid #cb0000;text-align:left}
384394
.demo{margin:18px 0 0;font-size:13px;color:#475569}.demo a{font-weight:600}
395+
details.alt{margin:6px 0 10px;text-align:left}
396+
details.alt summary{cursor:pointer;color:#cb0000;font-size:13px;font-weight:600;padding:6px 0}
397+
details.alt[open] summary{margin-bottom:8px}
385398
.browser{border:1px solid #cbd5e1;border-radius:10px;overflow:hidden;margin:4px 0 10px;text-align:left}
386399
.bpath{background:#0f172a;color:#93c5fd;font-family:ui-monospace,Menlo,monospace;font-size:12px;
387400
padding:9px 12px;word-break:break-all}

0 commit comments

Comments
 (0)