dsh-code-security 0.4.1 → 0.5.0

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  [历史英文记录](docs/CHANGELOG.en.md)
6
6
 
7
+ ## 0.5.0(2026-10-17)
8
+
9
+ - 设置面板顶部新增「首次使用」分步引导:一次只讲一步,支持上一步/下一步/跳过,看完记在本机不再出现。
10
+ - 引导内容与该插件的实际用法一致;可在开发者工具里清掉 `dsh-first-run:dsh-code-security:v1` 重新查看。
7
11
  ## 0.4.1 (2026-10-15)
8
12
 
9
13
  - 修复同一行同时命中高熵密钥和其他安全规则时,其他规则的证据仍带出原始字符串的问题;所有证据统一使用同一套高熵脱敏判断,也覆盖截断的字符串。
package/lib/client.js CHANGED
@@ -1,6 +1,57 @@
1
1
  // Browser source, distributed directly; the TypeScript build preserves this file.
2
2
  window.__ModuleLoader__.load({id:'dsh-code-security',factory:require=>{
3
3
  const React=require('react'),{useState,useEffect,useRef,useMemo}=React,h=React.createElement;
4
+ var __FR_KEY = "dsh-first-run:dsh-code-security:v1";
5
+ var __FR_TITLE = "代码安全 · 首次使用(4 步)";
6
+ var __FR_STEPS = [{"title":"先全仓扫一次","body":"点「扫描」或让助手跑一次全仓扫描建立基线;第一次结果可能很多,属正常。"},{"title":"之后只看增量","body":"日常用「审 diff」,只报本次改动新增的问题,噪音低得多。"},{"title":"修完复验","body":"修完后点复验,插件会对比上次结果,告诉你哪些真的关掉了。"},{"title":"基线要谨慎","body":"「接受基线」会把当前问题标记为已知,之后不再拦截;只在确认无害时使用。"}];
7
+ // ---- first-run guide (shared snippet) ----
8
+ var __FR_STYLE_ID = 'dsh-first-run-css';
9
+ var __FR_CSS = [
10
+ '.fr-card{border:1px solid var(--dsw-border,rgba(0,0,0,.10));border-radius:12px;padding:14px 16px;margin:0 0 14px;background:linear-gradient(180deg,rgba(59,130,246,.06),rgba(59,130,246,.02))}',
11
+ '.fr-head{display:flex;align-items:center;gap:8px;margin-bottom:6px}',
12
+ '.fr-dot{width:6px;height:6px;border-radius:50%;background:var(--dsw-accent,#3b82f6)}',
13
+ '.fr-title{font-weight:650;font-size:13.5px}',
14
+ '.fr-count{margin-left:auto;font-size:11px;color:var(--dsw-text-weak,#6b7280);font-variant-numeric:tabular-nums}',
15
+ '.fr-step{font-weight:600;font-size:13px;margin:2px 0 4px}',
16
+ '.fr-body{color:var(--dsw-text-weak,#4b5563);font-size:12.5px;line-height:1.65}',
17
+ '.fr-try{margin-top:8px;padding:7px 9px;border-radius:8px;background:rgba(59,130,246,.09);font-size:12px;display:flex;gap:8px}',
18
+ '.fr-try code{font-family:ui-monospace,monospace;white-space:pre-wrap;word-break:break-word}',
19
+ '.fr-bar{display:flex;gap:8px;align-items:center;margin-top:12px}',
20
+ '.fr-progress{display:flex;gap:4px;margin-right:auto}',
21
+ '.fr-seg{width:16px;height:3px;border-radius:2px;background:var(--dsw-border,rgba(0,0,0,.12))}',
22
+ '.fr-seg.on{background:var(--dsw-accent,#3b82f6)}',
23
+ '.fr-btn{border:1px solid var(--dsw-border,rgba(0,0,0,.14));background:transparent;color:inherit;border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer}',
24
+ '.fr-btn:hover{background:rgba(0,0,0,.04)}',
25
+ '.fr-btn.primary{background:var(--dsw-accent,#3b82f6);border-color:transparent;color:#fff}',
26
+ '.fr-btn.ghost{border-color:transparent;color:var(--dsw-text-weak,#6b7280)}'
27
+ ].join('\n');
28
+ function __frEnsureCss(){ if (typeof document === 'undefined' || document.getElementById(__FR_STYLE_ID)) return; var el = document.createElement('style'); el.id = __FR_STYLE_ID; el.textContent = __FR_CSS; document.head.appendChild(el); }
29
+ function __frRead(){ try { return localStorage.getItem(__FR_KEY) === 'done'; } catch (e) { return false; } }
30
+ function __frWrite(){ try { localStorage.setItem(__FR_KEY, 'done'); } catch (e) {} }
31
+ function __FirstRunCard(){
32
+ var ds = React.useState(__frRead), done = ds[0], setDone = ds[1];
33
+ var is = React.useState(0), index = is[0], setIndex = is[1];
34
+ React.useEffect(function(){ __frEnsureCss(); }, []);
35
+ if (done) return null;
36
+ var step = __FR_STEPS[index];
37
+ var last = index >= __FR_STEPS.length - 1;
38
+ var close = function(){ __frWrite(); setDone(true); };
39
+ var kids = [
40
+ React.createElement('div', { className: 'fr-head', key: 'h' }, React.createElement('span', { className: 'fr-dot' }), React.createElement('span', { className: 'fr-title' }, __FR_TITLE), React.createElement('span', { className: 'fr-count' }, (index + 1) + ' / ' + __FR_STEPS.length)),
41
+ React.createElement('div', { className: 'fr-step', key: 't' }, step.title),
42
+ React.createElement('div', { className: 'fr-body', key: 'b' }, step.body)
43
+ ];
44
+ if (step.tryIt) kids.push(React.createElement('div', { className: 'fr-try', key: 'y' }, React.createElement('span', null, '可以直接说'), React.createElement('code', null, '「' + step.tryIt + '」')));
45
+ var buttons = [React.createElement('div', { className: 'fr-progress', key: 'p' }, __FR_STEPS.map(function(_, i){ return React.createElement('span', { key: i, className: 'fr-seg' + (i <= index ? ' on' : '') }); }))];
46
+ buttons.push(React.createElement('button', { className: 'fr-btn ghost', type: 'button', onClick: close, key: 's' }, '跳过'));
47
+ if (index > 0) buttons.push(React.createElement('button', { className: 'fr-btn', type: 'button', key: 'prev', onClick: function(){ setIndex(index - 1); } }, '上一步'));
48
+ buttons.push(React.createElement('button', { className: 'fr-btn primary', type: 'button', key: 'next', onClick: function(){ if (last) close(); else setIndex(index + 1); } }, last ? '开始使用' : '下一步'));
49
+ kids.push(React.createElement('div', { className: 'fr-bar', key: 'bar' }, buttons));
50
+ return React.createElement('div', { className: 'fr-card' }, kids);
51
+ }
52
+ function __withFirstRun(Component){ return function FirstRunWrapped(props){ return React.createElement(React.Fragment, null, React.createElement(__FirstRunCard, null), React.createElement(Component, props)); }; }
53
+ // ---- end first-run guide ----
54
+
4
55
  let locale,sidebar;
5
56
  const english=()=>{try{return String(locale?.getSnapshot?.().active||'').startsWith('en')}catch{return false}};
6
57
  const t=(zh,en)=>english()?en:zh;
@@ -58,6 +109,6 @@ window.__ModuleLoader__.load({id:'dsh-code-security',factory:require=>{
58
109
  report?.hasScan?h(React.Fragment,null,h('div',{className:'secw-tools'},h('label',{htmlFor:'secw-filter'},t('问题筛选','Filter findings')),h('select',{id:'secw-filter',value:filter,onChange:e=>{setFilter(e.target.value);setLimit(50)}},h('option',{value:'all'},t('全部','All')),h('option',{value:'new'},t('仅新增','New only')),h('option',{value:'high'},t('高风险','High risk'))),h('button',{disabled:busy,onClick:()=>download('sarif')},t('下载 SARIF','Download SARIF')),h('button',{disabled:busy,onClick:()=>download('markdown')},t('下载 Markdown','Download Markdown'))),findings.length?findings.slice(0,limit).map(f=>h(FindingCard,{key:f.id,finding:f,fresh:fresh.has(f.id),onOpen:f=>location(f,false),onCopy:f=>location(f,true)})):h('p',{className:'secw-muted'},t('此筛选下没有问题。','No findings match this filter.')),findings.length>limit?h('button',{onClick:()=>setLimit(n=>n+50)},t('显示更多','Show more')):null,(report.findingsTotal??0)>200?h('p',{className:'secw-muted'},t('面板最多展示 200 项,完整结果请导出报告。','The panel shows up to 200 findings. Export the full report for all results.')):null):null):null);
59
110
  }
60
111
  const css=`.secw{max-width:1060px;min-width:0;margin:auto;font:14px/1.6 system-ui,sans-serif;color:var(--dsw-alias-label-primary,#253047)}.secw *{box-sizing:border-box}.secw h2{font-size:22px;margin:0}.secw h3{font-size:17px;margin:0 0 8px}.secw p{margin:7px 0}.secw label{display:block;margin:8px 0 4px}.secw button,.secw select,.secw input:not([type=checkbox]){font:inherit;color:inherit;border:1px solid var(--dsw-alias-border-l3,#d3dbe5);border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);padding:8px 10px;max-width:100%}.secw input:not([type=checkbox]),.secw select{width:100%;min-width:0}.secw button{cursor:pointer;white-space:normal}.secw button:disabled{opacity:.5;cursor:default}.secw .secw-primary{background:#245ee9;color:#fff;border-color:#245ee9}.secw .secw-tools,.secw .secw-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:12px 0}.secw .secw-tools select{width:auto;max-width:100%}.secw .secw-muted{color:var(--dsw-alias-label-secondary,#64718a);font-size:13px}.secw .secw-card,.secw .secw-finding{border:1px solid var(--dsw-alias-border-l3,#d3dbe5);border-radius:11px;padding:14px;margin:12px 0;overflow-wrap:anywhere;background:var(--dsw-alias-bg-layer-1,#fff)}.secw .secw-finding[data-severity=critical],.secw .secw-finding[data-severity=high]{border-left:4px solid #c04836}.secw .secw-coverage{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:8px;margin:10px 0}.secw .secw-engine{border:1px solid var(--dsw-alias-border-l3,#d3dbe5);border-radius:9px;padding:10px;overflow-wrap:anywhere}.secw .secw-engine strong{margin-right:8px}.secw .secw-engine small{display:block;color:var(--dsw-alias-label-secondary,#64718a)}.secw .secw-badge{display:inline-block;background:var(--dsw-alias-bg-layer-2,#f0f3f8);border-radius:5px;padding:1px 6px;font-size:12px}.secw .secw-badge[data-status=failed],.secw .secw-badge[data-status=partial]{color:#b33928}.secw .secw-error{background:#fff0ed;color:#973321;padding:10px;border-radius:8px;overflow-wrap:anywhere}.secw .secw-notice{background:#edf4ff;color:#17468c;padding:10px;border-radius:8px}.secw .secw-choice{display:flex;align-items:center;gap:8px}.secw fieldset{border:1px solid var(--dsw-alias-border-l3,#d3dbe5);border-radius:8px;margin:12px 0}.secw summary{cursor:pointer;font-weight:600}.secw pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:160px;overflow:auto;background:var(--dsw-alias-bg-layer-2,#f3f5f9);padding:10px;border-radius:6px}.secw .secw-file{font-family:ui-monospace,monospace;overflow-wrap:anywhere}.secw a{color:#245ee9}.secw :focus-visible{outline:3px solid #78a8ff;outline-offset:2px}[role=dialog]:has(.secw){width:min(1200px,calc(100vw - 48px))!important;max-width:1200px!important}@media(max-width:700px){[role=dialog]:has(.secw){width:100%!important;max-width:100%!important;flex-direction:column!important}[role=dialog]:has(.secw) nav{width:100%!important;flex-basis:auto!important;max-height:140px;overflow:auto;border-right:0}[role=dialog]:has(.secw) nav>div:has(>button){display:flex;flex-direction:row!important;flex-wrap:wrap;flex-shrink:0;gap:4px}[role=dialog]:has(.secw)>nav+div{min-width:0;width:100%}[role=dialog]:has(.secw)>nav+div>div:has(.secw){padding:12px!important;min-width:0}.secw .secw-coverage{grid-template-columns:1fr}}`;
61
- function apply(ctx){try{locale=ctx.get?.('locale')||ctx.locale}catch{locale=ctx.locale}try{sidebar=ctx.get?.('sidebarRight')||ctx.sidebarRight}catch{}ctx.slots.inject('settings.section',()=>ctx.slots.register({name:'settings.section',id:'dsh-code-security',order:59,label:()=>t('代码安全','Code security'),inject:()=>({})},Workbench))}
112
+ function apply(ctx){try{locale=ctx.get?.('locale')||ctx.locale}catch{locale=ctx.locale}try{sidebar=ctx.get?.('sidebarRight')||ctx.sidebarRight}catch{}ctx.slots.inject('settings.section',()=>ctx.slots.register({name:'settings.section',id:'dsh-code-security',order:59,label:()=>t('代码安全','Code security'),inject:()=>({})},__withFirstRun(Workbench)))}
62
113
  return{inject:['slots','locale'],apply};
63
114
  }});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-security",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "对代码与 Git 改动进行本地静态安全检查,并追踪修复结果。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -35,7 +35,9 @@
35
35
  "patch": "./cordis.patch.yml"
36
36
  },
37
37
  "client": {
38
- "inject": ["@deepseek-ai/dsh-client-ui-settings"],
38
+ "inject": [
39
+ "@deepseek-ai/dsh-client-ui-settings"
40
+ ],
39
41
  "platform": "web"
40
42
  }
41
43
  },