@vordsec/cli 0.1.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/LICENSE +661 -0
- package/README.md +33 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +86 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/explain.d.ts +20 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +61 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/guard.d.ts +41 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +106 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/hook.d.ts +66 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +127 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/ignore.d.ts +40 -0
- package/dist/commands/ignore.d.ts.map +1 -0
- package/dist/commands/ignore.js +159 -0
- package/dist/commands/ignore.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +75 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install-gate.d.ts +19 -0
- package/dist/commands/install-gate.d.ts.map +1 -0
- package/dist/commands/install-gate.js +118 -0
- package/dist/commands/install-gate.js.map +1 -0
- package/dist/commands/mcp.d.ts +53 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +155 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/scan.d.ts +21 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +77 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/exit-codes.d.ts +38 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +40 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/report/gate.d.ts +41 -0
- package/dist/report/gate.d.ts.map +1 -0
- package/dist/report/gate.js +111 -0
- package/dist/report/gate.js.map +1 -0
- package/dist/report/scan.d.ts +34 -0
- package/dist/report/scan.d.ts.map +1 -0
- package/dist/report/scan.js +170 -0
- package/dist/report/scan.js.map +1 -0
- package/dist/shim/artifacts.d.ts +54 -0
- package/dist/shim/artifacts.d.ts.map +1 -0
- package/dist/shim/artifacts.js +114 -0
- package/dist/shim/artifacts.js.map +1 -0
- package/dist/shim/decide.d.ts +68 -0
- package/dist/shim/decide.d.ts.map +1 -0
- package/dist/shim/decide.js +62 -0
- package/dist/shim/decide.js.map +1 -0
- package/dist/shim/install.d.ts +76 -0
- package/dist/shim/install.d.ts.map +1 -0
- package/dist/shim/install.js +174 -0
- package/dist/shim/install.js.map +1 -0
- package/dist/shim/log.d.ts +52 -0
- package/dist/shim/log.d.ts.map +1 -0
- package/dist/shim/log.js +78 -0
- package/dist/shim/log.js.map +1 -0
- package/dist/shim/run.d.ts +37 -0
- package/dist/shim/run.d.ts.map +1 -0
- package/dist/shim/run.js +100 -0
- package/dist/shim/run.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @vordsec/cli
|
|
2
|
+
|
|
3
|
+
**AI 에이전트가 위험한 패키지를 설치하려 할 때 막는 도구다.** 코드 스캔은 보조다.
|
|
4
|
+
전부 로컬에서 돈다. 서버가 없다.
|
|
5
|
+
|
|
6
|
+
전체 설명은 [레포 README](https://github.com/createrman343212/Vord#readme) 에 있다.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm i -g @vordsec/cli # 또는 설치 없이: npx @vordsec/cli …
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Node.js 20 이상이 필요하다.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
vord guard -- npm install lodash # 설치 명령을 검사한다 (판정만 한다. 실행하지 않는다)
|
|
16
|
+
vord scan # 이 프로젝트를 스캔한다 (--json · --sarif · --strict)
|
|
17
|
+
vord explain SEC-018 # 룰 하나를 사람 말로 설명한다
|
|
18
|
+
vord hook # Claude Code PreToolUse 훅 (stdin 으로 받는다)
|
|
19
|
+
vord mcp # MCP 서버 (stdio)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
종료 코드 — **0 통과 · 1 차단 · 2 확인 불가 · 3 사용법 오류.**
|
|
23
|
+
차단과 확인 불가를 가른다. 나가는 동작은 같아도 뜻이 다르고 대응도 다르다.
|
|
24
|
+
|
|
25
|
+
## 소스코드는 나가지 않는다
|
|
26
|
+
|
|
27
|
+
밖으로 나가는 것은 **설치 명령에 등장한 패키지 이름 하나뿐**이고, 갈 수 있는 곳은
|
|
28
|
+
`registry.npmjs.org` · `api.npmjs.org` · `pypi.org` 셋뿐이다. GET 만 쓰고 본문을 싣지 않는다.
|
|
29
|
+
테스트가 이것을 강제한다.
|
|
30
|
+
|
|
31
|
+
## 라이선스
|
|
32
|
+
|
|
33
|
+
AGPL-3.0-only.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `vord` 진입점.
|
|
4
|
+
*
|
|
5
|
+
* Tier 0 넷 — `guard` · `explain` · `init` · `install-gate`.
|
|
6
|
+
* Tier 1 둘 — `scan` · `ignore` (룰 4개가 생기고 나서 열렸다).
|
|
7
|
+
* **없는 커맨드를 도움말에 적지 않는다.**
|
|
8
|
+
*
|
|
9
|
+
* **`guard` 가 중심이다.** ADR-012 의 유통 3경로가 전부 그것을 부르고,
|
|
10
|
+
* `install-gate` 는 같은 판정을 PATH 를 가로채 자동으로 부르게 하는 설치 도우미다.
|
|
11
|
+
*/
|
|
12
|
+
import { runInstallGate } from './commands/install-gate.js';
|
|
13
|
+
import { parseScanArgs, runScan } from './commands/scan.js';
|
|
14
|
+
import { runIgnore } from './commands/ignore.js';
|
|
15
|
+
import { parseInitArgs, runInit } from './commands/init.js';
|
|
16
|
+
import { parseGuardArgs, runGuard } from './commands/guard.js';
|
|
17
|
+
import { runExplain } from './commands/explain.js';
|
|
18
|
+
import { runHook, readStdin } from './commands/hook.js';
|
|
19
|
+
import { runMcpServer } from './commands/mcp.js';
|
|
20
|
+
import { EXIT } from './exit-codes.js';
|
|
21
|
+
const [, , command, ...rest] = process.argv;
|
|
22
|
+
const emit = (lines, code) => {
|
|
23
|
+
for (const line of lines)
|
|
24
|
+
process.stdout.write(line + '\n');
|
|
25
|
+
process.exit(code);
|
|
26
|
+
};
|
|
27
|
+
if (command === 'guard') {
|
|
28
|
+
const r = await runGuard(parseGuardArgs(rest));
|
|
29
|
+
emit(r.lines, r.exitCode);
|
|
30
|
+
}
|
|
31
|
+
else if (command === 'hook') {
|
|
32
|
+
// Claude Code `PreToolUse`. **결정은 오직 JSON 으로 말하고 종료 코드는 항상 0 이다** —
|
|
33
|
+
// 다른 종료 코드 + 잘못된 JSON 은 "비차단 오류" 로 처리되어 도구가 그대로 실행된다
|
|
34
|
+
const r = await runHook(await readStdin(process.stdin));
|
|
35
|
+
if (r.output !== '')
|
|
36
|
+
process.stdout.write(r.output + '\n');
|
|
37
|
+
process.exit(r.exitCode);
|
|
38
|
+
}
|
|
39
|
+
else if (command === 'mcp') {
|
|
40
|
+
await runMcpServer(process.stdin, (line) => process.stdout.write(line));
|
|
41
|
+
process.exit(EXIT.OK);
|
|
42
|
+
}
|
|
43
|
+
else if (command === 'scan') {
|
|
44
|
+
const r = runScan(parseScanArgs(rest, process.cwd()));
|
|
45
|
+
emit(r.lines, r.exitCode);
|
|
46
|
+
}
|
|
47
|
+
else if (command === 'ignore') {
|
|
48
|
+
const r = runIgnore(rest, process.cwd());
|
|
49
|
+
emit(r.lines, r.exitCode);
|
|
50
|
+
}
|
|
51
|
+
else if (command === 'explain') {
|
|
52
|
+
const r = runExplain(rest);
|
|
53
|
+
emit(r.lines, r.exitCode);
|
|
54
|
+
}
|
|
55
|
+
else if (command === 'install-gate') {
|
|
56
|
+
const r = runInstallGate(rest, {
|
|
57
|
+
env: { PATH: process.env['PATH'] ?? '' },
|
|
58
|
+
cliRunPath: '@vordsec/cli',
|
|
59
|
+
});
|
|
60
|
+
emit(r.lines, r.exitCode);
|
|
61
|
+
}
|
|
62
|
+
else if (command === 'init') {
|
|
63
|
+
const r = await runInit(parseInitArgs(rest));
|
|
64
|
+
emit(r.lines, r.exitCode);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
emit([
|
|
68
|
+
'vord — AI 에이전트의 위험한 패키지 설치를 차단한다',
|
|
69
|
+
'',
|
|
70
|
+
' vord guard -- npm install <이름> 설치 명령을 검사한다 (판정만, 실행하지 않는다)',
|
|
71
|
+
' vord scan [경로] Tier 1 스캔 (--json · --sarif · --strict)',
|
|
72
|
+
' vord ignore add SEC-003 <경로> 오탐을 무시 처리한다 (사유 필수)',
|
|
73
|
+
' vord explain SEC-016 룰이 무엇을 보고 왜 그러는지',
|
|
74
|
+
' vord init 캐시 예열 (ADR-017)',
|
|
75
|
+
' vord install-gate --shell 설치 경계 shim (opt-in, ADR-026)',
|
|
76
|
+
'',
|
|
77
|
+
'에이전트가 붙는 자리 (직접 부르지 않는다 — 설정에 등록한다)',
|
|
78
|
+
' vord hook Claude Code PreToolUse. stdin 으로 JSON 을 받는다',
|
|
79
|
+
' vord mcp MCP 서버 (stdio). 도구 하나 — vord_guard',
|
|
80
|
+
'',
|
|
81
|
+
'종료 코드: 0 통과 · 1 차단·취약 · 2 확인 불가 · 3 사용법 오류',
|
|
82
|
+
' 스캔의 확인 불가는 기본 0 이다 — `--strict` 로만 2 가 된다',
|
|
83
|
+
'',
|
|
84
|
+
], command === undefined ? EXIT.OK : EXIT.USAGE);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord explain <RULE_ID>` — 룰이 무엇을 보고 왜 그러는지.
|
|
3
|
+
*
|
|
4
|
+
* **키 없이도 읽을 만해야 한다** (ADR-013). LLM 설명 계층은 선택적 상위 계층이고
|
|
5
|
+
* 기본 off 다 — 이 출력이 비면 기본 경험이 비는 것이다.
|
|
6
|
+
*
|
|
7
|
+
* 데이터는 `@vordsec/core` 의 `RULE_META` 다. 여기서는 배치만 한다.
|
|
8
|
+
*
|
|
9
|
+
* **구현된 룰만 나온다.** 카탈로그는 18개지만(HC-2) 구현은 Tier 0 셋뿐이고,
|
|
10
|
+
* 구현 없는 룰의 설명문은 검증할 수 없다. 없는 것을 있는 것처럼 보이게 하지 않는다.
|
|
11
|
+
*/
|
|
12
|
+
import { type RuleMeta } from '@vordsec/core';
|
|
13
|
+
import { type ExitCode } from '../exit-codes.js';
|
|
14
|
+
export interface ExplainResult {
|
|
15
|
+
readonly lines: readonly string[];
|
|
16
|
+
readonly exitCode: ExitCode;
|
|
17
|
+
}
|
|
18
|
+
export declare function renderRule(m: RuleMeta): readonly string[];
|
|
19
|
+
export declare function runExplain(argv: readonly string[]): ExplainResult;
|
|
20
|
+
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../src/commands/explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAOD,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,MAAM,EAAE,CAWzD;AAeD,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,CAkBjE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord explain <RULE_ID>` — 룰이 무엇을 보고 왜 그러는지.
|
|
3
|
+
*
|
|
4
|
+
* **키 없이도 읽을 만해야 한다** (ADR-013). LLM 설명 계층은 선택적 상위 계층이고
|
|
5
|
+
* 기본 off 다 — 이 출력이 비면 기본 경험이 비는 것이다.
|
|
6
|
+
*
|
|
7
|
+
* 데이터는 `@vordsec/core` 의 `RULE_META` 다. 여기서는 배치만 한다.
|
|
8
|
+
*
|
|
9
|
+
* **구현된 룰만 나온다.** 카탈로그는 18개지만(HC-2) 구현은 Tier 0 셋뿐이고,
|
|
10
|
+
* 구현 없는 룰의 설명문은 검증할 수 없다. 없는 것을 있는 것처럼 보이게 하지 않는다.
|
|
11
|
+
*/
|
|
12
|
+
import { RULE_META, findRuleMeta } from '@vordsec/core';
|
|
13
|
+
import { EXIT } from '../exit-codes.js';
|
|
14
|
+
function section(title, items) {
|
|
15
|
+
if (items.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
return ['', title, ...items.map((s) => ` · ${s}`)];
|
|
18
|
+
}
|
|
19
|
+
export function renderRule(m) {
|
|
20
|
+
return [
|
|
21
|
+
`${m.id} — ${m.title}`,
|
|
22
|
+
`Tier ${m.tier} · 판정: ${m.actions.join(' / ')}`,
|
|
23
|
+
...section('무엇을 보나', m.checks),
|
|
24
|
+
...section('왜 위험한가', m.why),
|
|
25
|
+
...section('걸렸으면 무엇을 하나', m.whatToDo),
|
|
26
|
+
...section('이 룰이 하지 않는 것', m.limits),
|
|
27
|
+
'',
|
|
28
|
+
`근거: ${m.adrs.join(' · ')}`,
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
function listRules() {
|
|
32
|
+
return [
|
|
33
|
+
'설명할 수 있는 룰',
|
|
34
|
+
'',
|
|
35
|
+
...RULE_META.map((m) => ` ${m.id} Tier ${m.tier} ${m.title}`),
|
|
36
|
+
'',
|
|
37
|
+
`카탈로그는 18개지만(SEC-001~018) **구현된 것은 위 ${RULE_META.length}개뿐이다.**`,
|
|
38
|
+
'나머지는 아직 탐지 로직이 없어 설명문을 검증할 수 없다.',
|
|
39
|
+
'',
|
|
40
|
+
'사용법: vord explain SEC-016',
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
export function runExplain(argv) {
|
|
44
|
+
const id = argv[0];
|
|
45
|
+
if (id === undefined || id === '--list') {
|
|
46
|
+
return { lines: listRules(), exitCode: EXIT.OK };
|
|
47
|
+
}
|
|
48
|
+
const meta = findRuleMeta(id);
|
|
49
|
+
if (meta === undefined) {
|
|
50
|
+
return {
|
|
51
|
+
lines: [
|
|
52
|
+
`vord explain: \`${id}\` 의 설명이 없다.`,
|
|
53
|
+
'',
|
|
54
|
+
...listRules(),
|
|
55
|
+
],
|
|
56
|
+
exitCode: EXIT.USAGE,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { lines: renderRule(meta), exitCode: EXIT.OK };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../src/commands/explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,IAAI,EAAiB,MAAM,kBAAkB,CAAC;AAOvD,SAAS,OAAO,CAAC,KAAa,EAAE,KAAwB;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAW;IACpC,OAAO;QACL,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE;QACtB,QAAQ,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QAC/C,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QAC9B,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC;QAC3B,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC;QACrC,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC;QACpC,EAAE;QACF,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED,SAAS,SAAS;IAChB,OAAO;QACL,YAAY;QACZ,EAAE;QACF,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;QAChE,EAAE;QACF,uCAAuC,SAAS,CAAC,MAAM,SAAS;QAChE,kCAAkC;QAClC,EAAE;QACF,2BAA2B;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;IACnD,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EAAE;gBACL,mBAAmB,EAAE,cAAc;gBACnC,EAAE;gBACF,GAAG,SAAS,EAAE;aACf;YACD,QAAQ,EAAE,IAAI,CAAC,KAAK;SACrB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord guard -- <설치 명령>` — Tier 0 진입점.
|
|
3
|
+
*
|
|
4
|
+
* **ADR-012 의 유통 3경로(`npx` · Claude Code 스킬 · MCP)가 전부 이걸 부른다.**
|
|
5
|
+
* shim(ADR-026)은 같은 판정을 PATH 를 가로채 자동으로 부르는 형태이고,
|
|
6
|
+
* 이 커맨드는 **명시적으로** 부르는 형태다. 판정 로직은 하나다 — `guardCommand`.
|
|
7
|
+
*
|
|
8
|
+
* ## `--` 를 요구한다
|
|
9
|
+
*
|
|
10
|
+
* `vord guard npm install --json` 에서 `--json` 은 누구 것인가. 우리 것인가 npm 것인가.
|
|
11
|
+
* 구분자가 없으면 이 질문에 답이 없고, 답이 없는 채로 두면 언젠가 사용자의 플래그를
|
|
12
|
+
* 우리가 먹는다. **`--` 앞은 우리 플래그, 뒤는 통째로 설치 명령**이다.
|
|
13
|
+
*
|
|
14
|
+
* ## 실행하지 않는다
|
|
15
|
+
*
|
|
16
|
+
* 이 커맨드는 **판정만 한다.** 통과해도 설치를 대신 실행하지 않는다 —
|
|
17
|
+
* 실행까지 하면 이 파일이 프로세스를 띄우게 되고, 그건 HC-1 정적 층이 shim 하나로
|
|
18
|
+
* 좁혀 둔 자리를 넓히는 일이다. 호출부가 종료 코드를 보고 스스로 실행한다.
|
|
19
|
+
*/
|
|
20
|
+
import { type GuardOptions } from '@vordsec/core';
|
|
21
|
+
import { type ExitCode } from '../exit-codes.js';
|
|
22
|
+
export interface GuardCliOptions {
|
|
23
|
+
/** `--` 뒤 전체. 이것이 검사 대상 명령이다 */
|
|
24
|
+
readonly command: readonly string[];
|
|
25
|
+
readonly json: boolean;
|
|
26
|
+
readonly cwd?: string;
|
|
27
|
+
/** 확인 불가를 통과시킨다 (ADR-017 장치 4). 차단은 통과시키지 않는다 */
|
|
28
|
+
readonly allowUnverified: boolean;
|
|
29
|
+
/** 인자 오류. 있으면 판정하지 않는다 */
|
|
30
|
+
readonly usageError?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface GuardCliResult {
|
|
33
|
+
readonly lines: readonly string[];
|
|
34
|
+
readonly exitCode: ExitCode;
|
|
35
|
+
}
|
|
36
|
+
export declare function parseGuardArgs(argv: readonly string[]): GuardCliOptions;
|
|
37
|
+
export declare function runGuard(opts: GuardCliOptions, deps?: {
|
|
38
|
+
readonly guardOptions?: GuardOptions;
|
|
39
|
+
}): Promise<GuardCliResult>;
|
|
40
|
+
export declare const GUARD_USAGE: string[];
|
|
41
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/commands/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAmBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CAwCvE;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,eAAe,EACrB,IAAI,GAAE;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAO,GAClD,OAAO,CAAC,cAAc,CAAC,CA4BzB;AAED,eAAO,MAAM,WAAW,UAAQ,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord guard -- <설치 명령>` — Tier 0 진입점.
|
|
3
|
+
*
|
|
4
|
+
* **ADR-012 의 유통 3경로(`npx` · Claude Code 스킬 · MCP)가 전부 이걸 부른다.**
|
|
5
|
+
* shim(ADR-026)은 같은 판정을 PATH 를 가로채 자동으로 부르는 형태이고,
|
|
6
|
+
* 이 커맨드는 **명시적으로** 부르는 형태다. 판정 로직은 하나다 — `guardCommand`.
|
|
7
|
+
*
|
|
8
|
+
* ## `--` 를 요구한다
|
|
9
|
+
*
|
|
10
|
+
* `vord guard npm install --json` 에서 `--json` 은 누구 것인가. 우리 것인가 npm 것인가.
|
|
11
|
+
* 구분자가 없으면 이 질문에 답이 없고, 답이 없는 채로 두면 언젠가 사용자의 플래그를
|
|
12
|
+
* 우리가 먹는다. **`--` 앞은 우리 플래그, 뒤는 통째로 설치 명령**이다.
|
|
13
|
+
*
|
|
14
|
+
* ## 실행하지 않는다
|
|
15
|
+
*
|
|
16
|
+
* 이 커맨드는 **판정만 한다.** 통과해도 설치를 대신 실행하지 않는다 —
|
|
17
|
+
* 실행까지 하면 이 파일이 프로세스를 띄우게 되고, 그건 HC-1 정적 층이 shim 하나로
|
|
18
|
+
* 좁혀 둔 자리를 넓히는 일이다. 호출부가 종료 코드를 보고 스스로 실행한다.
|
|
19
|
+
*/
|
|
20
|
+
import { guardCommand } from '@vordsec/core';
|
|
21
|
+
import { renderGate } from '../report/gate.js';
|
|
22
|
+
import { EXIT, exitCodeFor } from '../exit-codes.js';
|
|
23
|
+
const USAGE = [
|
|
24
|
+
'사용법: vord guard [옵션] -- <설치 명령>',
|
|
25
|
+
'',
|
|
26
|
+
' vord guard -- npm install lodash',
|
|
27
|
+
' vord guard --json -- pip install requests',
|
|
28
|
+
'',
|
|
29
|
+
'옵션',
|
|
30
|
+
' --json 기계가 읽는 출력 (사유 코드 포함)',
|
|
31
|
+
' --cwd <경로> 프로젝트 루트를 찾을 시작 위치',
|
|
32
|
+
' --allow-unverified 확인 불가를 통과시킨다. **차단은 통과시키지 않는다**',
|
|
33
|
+
'',
|
|
34
|
+
'`--` 가 필요하다 — 뒤에 오는 것을 통째로 설치 명령으로 본다.',
|
|
35
|
+
'그래야 `--json` 같은 플래그가 우리 것인지 매니저 것인지 갈린다.',
|
|
36
|
+
'',
|
|
37
|
+
'종료 코드: 0 통과 · 1 차단 · 2 확인 불가 · 3 사용법 오류',
|
|
38
|
+
];
|
|
39
|
+
export function parseGuardArgs(argv) {
|
|
40
|
+
const sep = argv.indexOf('--');
|
|
41
|
+
const base = {
|
|
42
|
+
json: false, allowUnverified: false, command: [],
|
|
43
|
+
};
|
|
44
|
+
if (sep < 0) {
|
|
45
|
+
return {
|
|
46
|
+
...base,
|
|
47
|
+
usageError: argv.length === 0
|
|
48
|
+
? '검사할 설치 명령이 없다.'
|
|
49
|
+
: '`--` 구분자가 없다. `vord guard -- npm install <이름>` 형태로 쓴다.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const ours = argv.slice(0, sep);
|
|
53
|
+
const command = argv.slice(sep + 1);
|
|
54
|
+
let json = false;
|
|
55
|
+
let allowUnverified = false;
|
|
56
|
+
let cwd;
|
|
57
|
+
for (let i = 0; i < ours.length; i++) {
|
|
58
|
+
const a = ours[i];
|
|
59
|
+
if (a === '--json')
|
|
60
|
+
json = true;
|
|
61
|
+
else if (a === '--allow-unverified')
|
|
62
|
+
allowUnverified = true;
|
|
63
|
+
else if (a === '--cwd') {
|
|
64
|
+
const v = ours[i + 1];
|
|
65
|
+
if (v === undefined)
|
|
66
|
+
return { ...base, json, allowUnverified, usageError: '`--cwd` 에 경로가 없다.' };
|
|
67
|
+
cwd = v;
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return { ...base, json, allowUnverified, usageError: `모르는 옵션: ${a}` };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (command.length === 0) {
|
|
75
|
+
return { ...base, json, allowUnverified, usageError: '`--` 뒤에 설치 명령이 없다.' };
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
command, json, allowUnverified, ...(cwd === undefined ? {} : { cwd }),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export async function runGuard(opts, deps = {}) {
|
|
82
|
+
if (opts.usageError !== undefined) {
|
|
83
|
+
return { lines: [`vord guard: ${opts.usageError}`, '', ...USAGE], exitCode: EXIT.USAGE };
|
|
84
|
+
}
|
|
85
|
+
// **인자를 재조립하는 것이 여기서는 맞다.** shim 은 원본 프로세스에 넘기려고
|
|
86
|
+
// 인자 배열을 그대로 유지하지만(따옴표가 깨진다), 파서의 입력은 명령 한 줄이다.
|
|
87
|
+
const line = opts.command.join(' ');
|
|
88
|
+
const report = await guardCommand(line, {
|
|
89
|
+
...(opts.cwd === undefined ? {} : { cwd: opts.cwd }),
|
|
90
|
+
allowUnverified: opts.allowUnverified,
|
|
91
|
+
...deps.guardOptions,
|
|
92
|
+
});
|
|
93
|
+
const lines = renderGate(report, opts.json);
|
|
94
|
+
if (!report.stop)
|
|
95
|
+
return { lines, exitCode: EXIT.OK };
|
|
96
|
+
const stopping = report.findings.filter((f) => f.action === 'block' || f.action === 'unverifiable');
|
|
97
|
+
return {
|
|
98
|
+
lines,
|
|
99
|
+
exitCode: exitCodeFor({
|
|
100
|
+
blocked: stopping.some((f) => f.action === 'block'),
|
|
101
|
+
unverifiable: stopping.some((f) => f.action === 'unverifiable'),
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export const GUARD_USAGE = USAGE;
|
|
106
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/commands/guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord hook` — Claude Code `PreToolUse` 어댑터. **Tier 0 의 본진** (ADR-012).
|
|
3
|
+
*
|
|
4
|
+
* ADR-012 는 이 자리를 이렇게 규정했다 — *"사람이 부르는 CLI 로는 설치 전을 잡을 수 없다."*
|
|
5
|
+
* `guard` 는 사람이 명시적으로 부르는 형태이고, 이것은 **에이전트가 설치를 실행하기 직전에
|
|
6
|
+
* 자동으로 끼어드는** 형태다. 판정은 같은 `guardCommand` 다 — 어댑터일 뿐이다.
|
|
7
|
+
*
|
|
8
|
+
* ## 계약에서 갈린 결정 셋
|
|
9
|
+
*
|
|
10
|
+
* ### 1. 통과일 때 **아무 결정도 내지 않는다**
|
|
11
|
+
*
|
|
12
|
+
* `permissionDecision: "allow"` 는 정상 권한 흐름을 **건너뛴다.** 그걸 통과에 쓰면
|
|
13
|
+
* Bash 승인을 받도록 설정한 사용자의 절차를 우리가 대신 꺼버린다.
|
|
14
|
+
* 우리가 할 말은 "막을 이유가 없다" 까지이지 "승인해도 된다" 가 아니다.
|
|
15
|
+
* 그래서 통과·비대상은 **결정 필드를 아예 싣지 않는다.**
|
|
16
|
+
*
|
|
17
|
+
* ### 2. 항상 종료 코드 0 으로 나간다
|
|
18
|
+
*
|
|
19
|
+
* 문서상 **다른 종료 코드 + 유효하지 않은 JSON 은 "비차단 오류" 로 처리되어 도구가 그대로
|
|
20
|
+
* 실행된다.** 즉 우리가 이상하게 죽으면 조용히 통과다. 그래서 결정은 **오직 JSON 으로만**
|
|
21
|
+
* 말하고 종료 코드는 항상 0 으로 고정한다 — 두 채널로 말하면 둘이 어긋나는 날이 온다.
|
|
22
|
+
*
|
|
23
|
+
* ### 3. 크래시는 통과시키고 **기록한다** (B-3 과 같은 답)
|
|
24
|
+
*
|
|
25
|
+
* ADR-026 이 shim 에서 마주친 것과 같은 물음이다. 게이트가 깨졌을 때 막을 것인가.
|
|
26
|
+
* 거기서 내린 잠정 결론(통과 + 기록)을 여기서도 쓴다 — **같은 물음에 다른 답을 두면
|
|
27
|
+
* 그게 곧 우회 경로다.** 사용자가 훅을 지우게 만드는 것이 한 번의 차단보다 나쁘다.
|
|
28
|
+
* 대신 `systemMessage` 로 "검사되지 않았다" 를 반드시 띄운다.
|
|
29
|
+
*/
|
|
30
|
+
import { guardCommand, type GuardOptions } from '@vordsec/core';
|
|
31
|
+
export interface HookInput {
|
|
32
|
+
readonly tool_name?: string;
|
|
33
|
+
readonly tool_input?: {
|
|
34
|
+
readonly command?: string;
|
|
35
|
+
};
|
|
36
|
+
readonly cwd?: string;
|
|
37
|
+
}
|
|
38
|
+
/** `hookSpecificOutput` 안에 들어가는 것 — 문서가 정한 이름 그대로 쓴다 */
|
|
39
|
+
export interface HookDecision {
|
|
40
|
+
readonly hookEventName: 'PreToolUse';
|
|
41
|
+
readonly permissionDecision?: 'deny';
|
|
42
|
+
readonly permissionDecisionReason?: string;
|
|
43
|
+
readonly additionalContext?: string;
|
|
44
|
+
readonly systemMessage?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface HookResult {
|
|
47
|
+
readonly output: string;
|
|
48
|
+
/** **항상 0 이다.** 위 규율 2 참조 */
|
|
49
|
+
readonly exitCode: 0;
|
|
50
|
+
}
|
|
51
|
+
export declare function parseHookInput(raw: string): HookInput | undefined;
|
|
52
|
+
export interface HookDeps {
|
|
53
|
+
readonly guardOptions?: GuardOptions;
|
|
54
|
+
/**
|
|
55
|
+
* 판정 함수. 테스트에서 주입한다 — **크래시 경로는 주입 없이 검사할 수 없다.**
|
|
56
|
+
* `guardCommand` 는 조회 실패를 내부에서 "확인 불가" 로 처리하므로 밖으로 던지지 않는다.
|
|
57
|
+
* `shim/run.ts` 가 같은 이유로 같은 자리를 열어 두고 있다.
|
|
58
|
+
*/
|
|
59
|
+
readonly guard?: (line: string, opts: GuardOptions) => Promise<GuardReport>;
|
|
60
|
+
}
|
|
61
|
+
type GuardReport = Awaited<ReturnType<typeof guardCommand>>;
|
|
62
|
+
export declare function runHook(raw: string, deps?: HookDeps): Promise<HookResult>;
|
|
63
|
+
/** stdin 을 통째로 읽는다. 훅 입력은 한 번에 온다 */
|
|
64
|
+
export declare function readStdin(stream: NodeJS.ReadableStream): Promise<string>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,YAAY,EAAgB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAM9E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAC;IACrC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;CACtB;AASD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAKjE;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7E;AAED,KAAK,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC;AAE5D,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CA4EnF;AAED,qCAAqC;AACrC,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAI9E"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vord hook` — Claude Code `PreToolUse` 어댑터. **Tier 0 의 본진** (ADR-012).
|
|
3
|
+
*
|
|
4
|
+
* ADR-012 는 이 자리를 이렇게 규정했다 — *"사람이 부르는 CLI 로는 설치 전을 잡을 수 없다."*
|
|
5
|
+
* `guard` 는 사람이 명시적으로 부르는 형태이고, 이것은 **에이전트가 설치를 실행하기 직전에
|
|
6
|
+
* 자동으로 끼어드는** 형태다. 판정은 같은 `guardCommand` 다 — 어댑터일 뿐이다.
|
|
7
|
+
*
|
|
8
|
+
* ## 계약에서 갈린 결정 셋
|
|
9
|
+
*
|
|
10
|
+
* ### 1. 통과일 때 **아무 결정도 내지 않는다**
|
|
11
|
+
*
|
|
12
|
+
* `permissionDecision: "allow"` 는 정상 권한 흐름을 **건너뛴다.** 그걸 통과에 쓰면
|
|
13
|
+
* Bash 승인을 받도록 설정한 사용자의 절차를 우리가 대신 꺼버린다.
|
|
14
|
+
* 우리가 할 말은 "막을 이유가 없다" 까지이지 "승인해도 된다" 가 아니다.
|
|
15
|
+
* 그래서 통과·비대상은 **결정 필드를 아예 싣지 않는다.**
|
|
16
|
+
*
|
|
17
|
+
* ### 2. 항상 종료 코드 0 으로 나간다
|
|
18
|
+
*
|
|
19
|
+
* 문서상 **다른 종료 코드 + 유효하지 않은 JSON 은 "비차단 오류" 로 처리되어 도구가 그대로
|
|
20
|
+
* 실행된다.** 즉 우리가 이상하게 죽으면 조용히 통과다. 그래서 결정은 **오직 JSON 으로만**
|
|
21
|
+
* 말하고 종료 코드는 항상 0 으로 고정한다 — 두 채널로 말하면 둘이 어긋나는 날이 온다.
|
|
22
|
+
*
|
|
23
|
+
* ### 3. 크래시는 통과시키고 **기록한다** (B-3 과 같은 답)
|
|
24
|
+
*
|
|
25
|
+
* ADR-026 이 shim 에서 마주친 것과 같은 물음이다. 게이트가 깨졌을 때 막을 것인가.
|
|
26
|
+
* 거기서 내린 잠정 결론(통과 + 기록)을 여기서도 쓴다 — **같은 물음에 다른 답을 두면
|
|
27
|
+
* 그게 곧 우회 경로다.** 사용자가 훅을 지우게 만드는 것이 한 번의 차단보다 나쁘다.
|
|
28
|
+
* 대신 `systemMessage` 로 "검사되지 않았다" 를 반드시 띄운다.
|
|
29
|
+
*/
|
|
30
|
+
import { guardCommand, stopsInstall } from '@vordsec/core';
|
|
31
|
+
import { formatFindings } from '../report/gate.js';
|
|
32
|
+
/** 우리가 보는 도구. 설치는 셸을 타고 들어온다 */
|
|
33
|
+
const WATCHED_TOOL = 'Bash';
|
|
34
|
+
function wrap(decision) {
|
|
35
|
+
// 결정이 없으면 **아무것도 출력하지 않는다.** 빈 JSON 도 파싱 대상이 되고,
|
|
36
|
+
// 파싱되는 것이 늘수록 계약이 어긋날 자리가 는다
|
|
37
|
+
if (decision === undefined)
|
|
38
|
+
return { output: '', exitCode: 0 };
|
|
39
|
+
return { output: JSON.stringify({ hookSpecificOutput: decision }), exitCode: 0 };
|
|
40
|
+
}
|
|
41
|
+
export function parseHookInput(raw) {
|
|
42
|
+
try {
|
|
43
|
+
const v = JSON.parse(raw);
|
|
44
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function runHook(raw, deps = {}) {
|
|
51
|
+
const input = parseHookInput(raw);
|
|
52
|
+
// 입력을 못 읽었다 — 우리 문제다. **막지 않고 알린다** (규율 3)
|
|
53
|
+
if (input === undefined) {
|
|
54
|
+
return wrap({
|
|
55
|
+
hookEventName: 'PreToolUse',
|
|
56
|
+
systemMessage: 'vord: 훅 입력을 읽지 못해 이 명령은 검사되지 않았다.',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// 우리가 볼 도구가 아니다. **조용히 지나간다** — 매 도구 호출마다 말을 얹으면 소음이 된다
|
|
60
|
+
if (input.tool_name !== WATCHED_TOOL)
|
|
61
|
+
return wrap(undefined);
|
|
62
|
+
const command = input.tool_input?.command;
|
|
63
|
+
if (typeof command !== 'string' || command.trim() === '')
|
|
64
|
+
return wrap(undefined);
|
|
65
|
+
let report;
|
|
66
|
+
try {
|
|
67
|
+
const guard = deps.guard ?? guardCommand;
|
|
68
|
+
report = await guard(command, {
|
|
69
|
+
...(input.cwd === undefined ? {} : { cwd: input.cwd }),
|
|
70
|
+
...deps.guardOptions,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
// **B-3 과 같은 답: 통과시키되 반드시 알린다**
|
|
75
|
+
const why = e instanceof Error ? e.message : String(e);
|
|
76
|
+
return wrap({
|
|
77
|
+
hookEventName: 'PreToolUse',
|
|
78
|
+
systemMessage: `vord: 검사에 실패해 그대로 진행한다 (${why}). 이 설치는 검사되지 않았다.`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// 설치 명령이 아니다 — `Bash` 호출 대다수가 여기서 나간다
|
|
82
|
+
if (report.notApplicable !== undefined)
|
|
83
|
+
return wrap(undefined);
|
|
84
|
+
const detail = formatFindings(report.findings).join('\n');
|
|
85
|
+
if (report.stop) {
|
|
86
|
+
const stopping = report.findings.filter((f) => stopsInstall(f.action));
|
|
87
|
+
const onlyUnverifiable = stopping.length > 0
|
|
88
|
+
&& stopping.every((f) => f.action === 'unverifiable');
|
|
89
|
+
// **차단과 확인 불가를 문장으로 가른다.** 훅에서는 둘 다 deny 라 등급이 뭉개지는데,
|
|
90
|
+
// 에이전트는 사유를 읽고 다음 행동을 정한다 (ADR-010 §2) — 문장이 곧 계약이다
|
|
91
|
+
const reason = onlyUnverifiable
|
|
92
|
+
? [
|
|
93
|
+
'vord: 확인하지 못해 설치를 멈췄다. **위험해서 막은 것이 아니다.**',
|
|
94
|
+
detail,
|
|
95
|
+
'다시 시도하거나 `vord init` 으로 캐시를 채운다. 그래도 막히면 `--ignore-scripts` 로',
|
|
96
|
+
'설치하거나, 판단을 건너뛰려면 `vord guard --allow-unverified` 로 확인한 뒤 진행한다.',
|
|
97
|
+
].join('\n')
|
|
98
|
+
: [
|
|
99
|
+
'vord: 이 설치는 위험하다고 판단했다.',
|
|
100
|
+
detail,
|
|
101
|
+
'같은 명령을 다시 실행하지 않는다. 패키지 이름이 실재하는지 먼저 확인한다.',
|
|
102
|
+
].join('\n');
|
|
103
|
+
return wrap({
|
|
104
|
+
hookEventName: 'PreToolUse',
|
|
105
|
+
permissionDecision: 'deny',
|
|
106
|
+
permissionDecisionReason: reason,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// 경고는 막지 않는다. 다만 **에이전트가 볼 수 있게 컨텍스트로 넘긴다** —
|
|
110
|
+
// 경고를 아무도 안 보면 그건 없는 것과 같다 (ADR-004)
|
|
111
|
+
const warned = report.findings.filter((f) => f.action === 'warn');
|
|
112
|
+
if (warned.length > 0) {
|
|
113
|
+
return wrap({
|
|
114
|
+
hookEventName: 'PreToolUse',
|
|
115
|
+
additionalContext: `vord: 막지는 않았지만 볼 것이 있다.\n${detail}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return wrap(undefined);
|
|
119
|
+
}
|
|
120
|
+
/** stdin 을 통째로 읽는다. 훅 입력은 한 번에 온다 */
|
|
121
|
+
export async function readStdin(stream) {
|
|
122
|
+
const chunks = [];
|
|
123
|
+
for await (const c of stream)
|
|
124
|
+
chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(String(c)));
|
|
125
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Finding, RuleId } from '@vordsec/core';
|
|
2
|
+
import { type ExitCode } from '../exit-codes.js';
|
|
3
|
+
export declare const IGNORE_FILE = ".vordignore";
|
|
4
|
+
export interface IgnoreEntry {
|
|
5
|
+
readonly rule: RuleId;
|
|
6
|
+
/** 경로 패턴. `*` 는 한 조각, `**` 는 여러 조각 */
|
|
7
|
+
readonly pattern: string;
|
|
8
|
+
readonly reason?: string;
|
|
9
|
+
/** 파일에서의 줄 번호 (1부터) */
|
|
10
|
+
readonly line: number;
|
|
11
|
+
}
|
|
12
|
+
/** 한 줄 파싱. 주석·빈 줄은 `undefined` */
|
|
13
|
+
export declare function parseIgnoreLine(raw: string, line: number): IgnoreEntry | undefined;
|
|
14
|
+
export declare function parseIgnoreFile(text: string): readonly IgnoreEntry[];
|
|
15
|
+
export declare function readIgnoreFile(root: string): readonly IgnoreEntry[];
|
|
16
|
+
/** `*` 는 한 조각, `**` 는 여러 조각. 나머지는 글자 그대로 */
|
|
17
|
+
export declare function matchesPattern(pattern: string, filePath: string): boolean;
|
|
18
|
+
export interface SuppressResult {
|
|
19
|
+
readonly kept: readonly Finding[];
|
|
20
|
+
readonly suppressed: readonly Finding[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 무시 규칙을 적용한다. **거른 것을 버리지 않고 돌려준다** — 리포터가 건수를 센다.
|
|
24
|
+
*
|
|
25
|
+
* ## 교차 항목도 같이 꺼진다 (ADR-024)
|
|
26
|
+
*
|
|
27
|
+
* 교차 항목은 **출처 룰의 파급**이고 `rule`·`location` 이 출처와 같다.
|
|
28
|
+
* 사용자가 그 출처를 오탐이라고 판단했으면 *"그래서 SEC-001 판정이 무효다"* 도 성립하지 않는다.
|
|
29
|
+
* 남겨 두면 **끈 적 없는 경고가 계속 뜨고**, 그건 무시 기능이 없는 것과 같다.
|
|
30
|
+
*
|
|
31
|
+
* 반대 방향은 자연히 막힌다 — 대상 룰(`SEC-001`)을 끄는 규칙은 교차 항목의 `rule`
|
|
32
|
+
* (출처인 `SEC-003`)과 안 맞아서 걸리지 않는다.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyIgnores(findings: readonly Finding[], entries: readonly IgnoreEntry[]): SuppressResult;
|
|
35
|
+
export interface IgnoreResult {
|
|
36
|
+
readonly lines: readonly string[];
|
|
37
|
+
readonly exitCode: ExitCode;
|
|
38
|
+
}
|
|
39
|
+
export declare function runIgnore(argv: readonly string[], cwd: string): IgnoreResult;
|
|
40
|
+
//# sourceMappingURL=ignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.d.ts","sourceRoot":"","sources":["../../src/commands/ignore.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,kCAAkC;AAClC,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAclF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,WAAW,EAAE,CAIpE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,WAAW,EAAE,CAMnE;AAED,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAczE;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS,OAAO,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,SAAS,WAAW,EAAE,GAC9B,cAAc,CAUhB;AAID,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAYD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAoD5E"}
|