@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
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shim 설치 · 제거 · 확인 — `vord install-gate --shell` (ADR-026 결정 3).
|
|
3
|
+
*
|
|
4
|
+
* **opt-in 이다.** 사용자가 명시적으로 깔 때만 설치한다.
|
|
5
|
+
* 설치·제거·PATH 확인을 같은 커맨드가 책임진다 — 깔 수만 있고 뺄 수 없으면
|
|
6
|
+
* 사용자는 shim 을 쓰지 않는다.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { RUNTIME_FILENAME, SHIM_EXTENSIONS, SHIM_MANAGERS, buildArtifact, expectedFilenames, } from './artifacts.js';
|
|
12
|
+
export const DEFAULT_SHIM_DIRNAME = path.join('.vord', 'shim');
|
|
13
|
+
export function defaultShimDir(home = os.homedir()) {
|
|
14
|
+
return path.join(home, DEFAULT_SHIM_DIRNAME);
|
|
15
|
+
}
|
|
16
|
+
/** PATH 를 나눈다. 구분자는 플랫폼마다 다르다 */
|
|
17
|
+
export function splitPath(pathEnv, delimiter = path.delimiter) {
|
|
18
|
+
return pathEnv.split(delimiter).filter((p) => p.trim() !== '');
|
|
19
|
+
}
|
|
20
|
+
function samePath(a, b) {
|
|
21
|
+
const norm = (p) => path.resolve(p).replace(/[\\/]+$/, '').toLowerCase();
|
|
22
|
+
return norm(a) === norm(b);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* PATH 에서 **shim 디렉터리를 빼고** 원본을 찾는다.
|
|
26
|
+
*
|
|
27
|
+
* shim 이 이미 깔린 뒤에 다시 설치하면 shim 이 자기 자신을 원본으로 박는다 —
|
|
28
|
+
* 무한 재귀다. 그래서 항상 자기 디렉터리를 빼고 찾는다.
|
|
29
|
+
*/
|
|
30
|
+
export function findRealBinary(manager, extension, env, shimDir, exists = (p) => fs.existsSync(p)) {
|
|
31
|
+
const filename = `${manager}${extension}`;
|
|
32
|
+
for (const dir of splitPath(env.PATH)) {
|
|
33
|
+
if (samePath(dir, shimDir))
|
|
34
|
+
continue;
|
|
35
|
+
const candidate = path.join(dir, filename);
|
|
36
|
+
if (exists(candidate))
|
|
37
|
+
return candidate;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
export function planInstall(env, shimDir, managers = SHIM_MANAGERS, exists = (p) => fs.existsSync(p)) {
|
|
42
|
+
const entries = [];
|
|
43
|
+
const silentGaps = [];
|
|
44
|
+
for (const manager of managers) {
|
|
45
|
+
const found = SHIM_EXTENSIONS
|
|
46
|
+
.map((extension) => ({
|
|
47
|
+
extension,
|
|
48
|
+
realPath: findRealBinary(manager, extension, env, shimDir, exists),
|
|
49
|
+
}));
|
|
50
|
+
const anyFound = found.some((f) => f.realPath !== undefined);
|
|
51
|
+
for (const f of found) {
|
|
52
|
+
entries.push(f.realPath === undefined
|
|
53
|
+
? { manager, extension: f.extension, missing: true }
|
|
54
|
+
: { manager, extension: f.extension, realPath: f.realPath, missing: false });
|
|
55
|
+
// 그 매니저가 설치돼 있는데 확장자 하나만 없으면 **그 경로가 조용히 통과한다**
|
|
56
|
+
if (anyFound && f.realPath === undefined) {
|
|
57
|
+
silentGaps.push(`${manager}${f.extension}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { shimDir, entries, silentGaps };
|
|
62
|
+
}
|
|
63
|
+
/** 런타임 진입점 본문. 빌드된 CLI 를 부르는 얇은 파일이다 */
|
|
64
|
+
export function runtimeSource(cliRunPath) {
|
|
65
|
+
return `#!/usr/bin/env node
|
|
66
|
+
// vord 설치 경계 shim 런타임 (ADR-026). 자동 생성 — 직접 고치지 않는다.
|
|
67
|
+
import { runShim } from ${JSON.stringify(cliRunPath)};
|
|
68
|
+
const [, , manager, realPath, ...rest] = process.argv;
|
|
69
|
+
runShim({ manager, realPath, argv: rest });
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
export function install(env, opts) {
|
|
73
|
+
const shimDir = opts.shimDir ?? defaultShimDir();
|
|
74
|
+
const plan = planInstall(env, shimDir, opts.managers ?? SHIM_MANAGERS);
|
|
75
|
+
fs.mkdirSync(shimDir, { recursive: true });
|
|
76
|
+
const runtimePath = path.join(shimDir, RUNTIME_FILENAME);
|
|
77
|
+
fs.writeFileSync(runtimePath, runtimeSource(opts.cliRunPath));
|
|
78
|
+
const written = [];
|
|
79
|
+
const skipped = [];
|
|
80
|
+
for (const e of plan.entries) {
|
|
81
|
+
if (e.missing || e.realPath === undefined) {
|
|
82
|
+
skipped.push(`${e.manager}${e.extension}`);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const art = buildArtifact({ manager: e.manager, extension: e.extension, realPath: e.realPath }, runtimePath);
|
|
86
|
+
const dest = path.join(shimDir, art.filename);
|
|
87
|
+
fs.writeFileSync(dest, art.contents);
|
|
88
|
+
if (art.executable) {
|
|
89
|
+
try {
|
|
90
|
+
fs.chmodSync(dest, 0o755);
|
|
91
|
+
}
|
|
92
|
+
catch { /* Windows 는 무시 */ }
|
|
93
|
+
}
|
|
94
|
+
written.push(art.filename);
|
|
95
|
+
}
|
|
96
|
+
const onPath = isShimFirst(env, shimDir);
|
|
97
|
+
return {
|
|
98
|
+
shimDir,
|
|
99
|
+
written,
|
|
100
|
+
skipped,
|
|
101
|
+
silentGaps: plan.silentGaps,
|
|
102
|
+
onPath,
|
|
103
|
+
instructions: onPath ? [] : pathInstructions(shimDir),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* shim 디렉터리가 PATH 에 있고, **원본보다 앞인가.**
|
|
108
|
+
* 뒤에 있으면 아무것도 안 걸린다 — 있는 것과 없는 것이 같다.
|
|
109
|
+
*/
|
|
110
|
+
export function isShimFirst(env, shimDir, manager = 'npm') {
|
|
111
|
+
const dirs = splitPath(env.PATH);
|
|
112
|
+
const shimIndex = dirs.findIndex((d) => samePath(d, shimDir));
|
|
113
|
+
if (shimIndex === -1)
|
|
114
|
+
return false;
|
|
115
|
+
const realIndex = dirs.findIndex((d, i) => i !== shimIndex
|
|
116
|
+
&& SHIM_EXTENSIONS.some((e) => fs.existsSync(path.join(d, `${manager}${e}`))));
|
|
117
|
+
return realIndex === -1 || shimIndex < realIndex;
|
|
118
|
+
}
|
|
119
|
+
export function pathInstructions(shimDir) {
|
|
120
|
+
return [
|
|
121
|
+
'shim 디렉터리를 PATH **맨 앞**에 넣어야 걸린다. 뒤에 있으면 아무것도 안 걸린다.',
|
|
122
|
+
'',
|
|
123
|
+
` bash/zsh export PATH="${shimDir}:$PATH"`,
|
|
124
|
+
` PowerShell $env:PATH = "${shimDir};" + $env:PATH`,
|
|
125
|
+
` cmd set PATH=${shimDir};%PATH%`,
|
|
126
|
+
'',
|
|
127
|
+
'셸 설정 파일에 넣어야 새 터미널에서도 유지된다.',
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 우리가 만든 것만 지운다.
|
|
132
|
+
* **사용자가 그 디렉터리에 둔 다른 파일은 건드리지 않는다** — 디렉터리째 지우지 않는다.
|
|
133
|
+
*/
|
|
134
|
+
export function remove(shimDir) {
|
|
135
|
+
const ours = [...expectedFilenames(), RUNTIME_FILENAME];
|
|
136
|
+
const removed = [];
|
|
137
|
+
for (const name of ours) {
|
|
138
|
+
const p = path.join(shimDir, name);
|
|
139
|
+
try {
|
|
140
|
+
if (fs.existsSync(p)) {
|
|
141
|
+
fs.unlinkSync(p);
|
|
142
|
+
removed.push(name);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch { /* 못 지운 것은 remaining 으로 남는다 */ }
|
|
146
|
+
}
|
|
147
|
+
let remaining = [];
|
|
148
|
+
try {
|
|
149
|
+
remaining = fs.readdirSync(shimDir);
|
|
150
|
+
}
|
|
151
|
+
catch { /* 없으면 빈 것 */ }
|
|
152
|
+
return { shimDir, removed, remaining };
|
|
153
|
+
}
|
|
154
|
+
export function verify(env, shimDir) {
|
|
155
|
+
let files = [];
|
|
156
|
+
try {
|
|
157
|
+
files = fs.readdirSync(shimDir);
|
|
158
|
+
}
|
|
159
|
+
catch { /* 없으면 빈 것 */ }
|
|
160
|
+
const expected = expectedFilenames();
|
|
161
|
+
const installed = expected.filter((f) => files.includes(f));
|
|
162
|
+
const missing = expected.filter((f) => !files.includes(f));
|
|
163
|
+
const runtimePresent = files.includes(RUNTIME_FILENAME);
|
|
164
|
+
const onPath = isShimFirst(env, shimDir);
|
|
165
|
+
return {
|
|
166
|
+
shimDir,
|
|
167
|
+
installed,
|
|
168
|
+
missing,
|
|
169
|
+
runtimePresent,
|
|
170
|
+
onPath,
|
|
171
|
+
healthy: missing.length === 0 && runtimePresent && onPath,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/shim/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 무슨 일이 있었나. **`gate-crash` 와 `lookup-failed` 가 갈려 있는 것이 이 파일의 존재 이유다.**
|
|
3
|
+
*/
|
|
4
|
+
export type ShimOutcome =
|
|
5
|
+
/** 게이트가 막았다 — 위험하다고 판단 */
|
|
6
|
+
'blocked'
|
|
7
|
+
/** 조회를 못 해서 막았다 (ADR-017). **위험해서 막은 것이 아니다** */
|
|
8
|
+
| 'unverifiable'
|
|
9
|
+
/** 통과시켰다 */
|
|
10
|
+
| 'passed'
|
|
11
|
+
/** 게이트 대상이 아닌 명령이었다 (`npm run build` 등) */
|
|
12
|
+
| 'not-applicable'
|
|
13
|
+
/** **게이트 코드가 예외를 던졌다** — B-3 이 정해야 할 자리 */
|
|
14
|
+
| 'gate-crash'
|
|
15
|
+
/** shim 런타임 자체를 못 띄웠다. 래퍼가 원본으로 넘겼다 */
|
|
16
|
+
| 'shim-unavailable';
|
|
17
|
+
export interface ShimLogRecord {
|
|
18
|
+
/** ISO. **주입한다** — 시계를 직접 읽으면 테스트가 재현되지 않는다 */
|
|
19
|
+
readonly at: string;
|
|
20
|
+
readonly manager: string;
|
|
21
|
+
readonly outcome: ShimOutcome;
|
|
22
|
+
/** 명령에서 뽑은 **패키지 이름만.** argv 원문은 남기지 않는다 */
|
|
23
|
+
readonly packages: readonly string[];
|
|
24
|
+
/** 게이트가 통과시키지 않은 이유 요약. 크래시면 오류 종류 */
|
|
25
|
+
readonly detail?: string;
|
|
26
|
+
/** 게이트 판정에 걸린 시간(ms) — 지연이 곧 사용성이다 (ADR-013 §2) */
|
|
27
|
+
readonly ms?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const LOG_FILENAME = "shim-events.jsonl";
|
|
30
|
+
/** 무한히 자라지 않게. 넘으면 앞에서 자른다 */
|
|
31
|
+
export declare const MAX_LOG_BYTES: number;
|
|
32
|
+
export interface ShimLogger {
|
|
33
|
+
append(record: ShimLogRecord): void;
|
|
34
|
+
read(): readonly ShimLogRecord[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 한 줄씩 덧붙인다.
|
|
38
|
+
*
|
|
39
|
+
* **기록 실패는 삼킨다.** 로그를 못 써서 게이트가 죽으면 그게 곧 툴체인 브릭이고,
|
|
40
|
+
* 브릭은 사용자가 shim 을 지우게 만든다 — 영구 우회다.
|
|
41
|
+
*/
|
|
42
|
+
export declare function fileLogger(dir: string): ShimLogger;
|
|
43
|
+
export declare const NO_LOG: ShimLogger;
|
|
44
|
+
/** B-3 판단에 쓸 집계 — 크래시가 얼마나 자주, 왜 나는가 */
|
|
45
|
+
export interface ShimStats {
|
|
46
|
+
readonly total: number;
|
|
47
|
+
readonly byOutcome: Readonly<Record<ShimOutcome, number>>;
|
|
48
|
+
/** 크래시 사유별 건수. **이게 B-3 의 입력이다** */
|
|
49
|
+
readonly crashDetails: Readonly<Record<string, number>>;
|
|
50
|
+
}
|
|
51
|
+
export declare function summarize(records: readonly ShimLogRecord[]): ShimStats;
|
|
52
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/shim/log.ts"],"names":[],"mappings":"AAaA;;GAEG;AACH,MAAM,MAAM,WAAW;AACrB,0BAA0B;AACxB,SAAS;AACX,iDAAiD;GAC/C,cAAc;AAChB,YAAY;GACV,QAAQ;AACV,2CAA2C;GACzC,gBAAgB;AAClB,2CAA2C;GACzC,YAAY;AACd,uCAAuC;GACrC,kBAAkB,CAAC;AAEvB,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAEhD,8BAA8B;AAC9B,eAAO,MAAM,aAAa,QAAkB,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,IAAI,IAAI,SAAS,aAAa,EAAE,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CA0BlD;AAED,eAAO,MAAM,MAAM,EAAE,UAA8D,CAAC;AAEpF,wCAAwC;AACxC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,oCAAoC;IACpC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CActE"}
|
package/dist/shim/log.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shim 실행 기록 — **B-3 판단의 근거를 만든다** (ADR-026).
|
|
3
|
+
*
|
|
4
|
+
* ADR-026 이 크래시 정책을 미결로 남기면서 제약 하나를 고정했다:
|
|
5
|
+
* **shim 은 자기 크래시와 조회 실패를 구분해 기록해야 한다.**
|
|
6
|
+
* 구분이 없으면 나중에 그 판단의 근거를 만들 수 없다.
|
|
7
|
+
*
|
|
8
|
+
* 로컬 파일에만 쓴다. 네트워크로 보내지 않는다 (HC-1).
|
|
9
|
+
* **argv 원문을 남기지 않는다** — 설치 명령에는 사설 레지스트리 토큰이 붙기도 한다.
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
export const LOG_FILENAME = 'shim-events.jsonl';
|
|
14
|
+
/** 무한히 자라지 않게. 넘으면 앞에서 자른다 */
|
|
15
|
+
export const MAX_LOG_BYTES = 2 * 1024 * 1024;
|
|
16
|
+
/**
|
|
17
|
+
* 한 줄씩 덧붙인다.
|
|
18
|
+
*
|
|
19
|
+
* **기록 실패는 삼킨다.** 로그를 못 써서 게이트가 죽으면 그게 곧 툴체인 브릭이고,
|
|
20
|
+
* 브릭은 사용자가 shim 을 지우게 만든다 — 영구 우회다.
|
|
21
|
+
*/
|
|
22
|
+
export function fileLogger(dir) {
|
|
23
|
+
const file = path.join(dir, LOG_FILENAME);
|
|
24
|
+
return {
|
|
25
|
+
append(record) {
|
|
26
|
+
try {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
let size = 0;
|
|
29
|
+
try {
|
|
30
|
+
size = fs.statSync(file).size;
|
|
31
|
+
}
|
|
32
|
+
catch { /* 없으면 0 */ }
|
|
33
|
+
if (size > MAX_LOG_BYTES) {
|
|
34
|
+
const kept = fs.readFileSync(file, 'utf8').split('\n').slice(-2000).join('\n');
|
|
35
|
+
fs.writeFileSync(file, kept);
|
|
36
|
+
}
|
|
37
|
+
fs.appendFileSync(file, JSON.stringify(record) + '\n');
|
|
38
|
+
}
|
|
39
|
+
catch { /* 못 써도 게이트는 계속 돈다 */ }
|
|
40
|
+
},
|
|
41
|
+
read() {
|
|
42
|
+
try {
|
|
43
|
+
return fs.readFileSync(file, 'utf8')
|
|
44
|
+
.split('\n')
|
|
45
|
+
.filter((l) => l.trim() !== '')
|
|
46
|
+
.flatMap((l) => {
|
|
47
|
+
try {
|
|
48
|
+
return [JSON.parse(l)];
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export const NO_LOG = { append() { }, read() { return []; } };
|
|
62
|
+
export function summarize(records) {
|
|
63
|
+
const byOutcome = {
|
|
64
|
+
blocked: 0, unverifiable: 0, passed: 0, 'not-applicable': 0,
|
|
65
|
+
'gate-crash': 0, 'shim-unavailable': 0,
|
|
66
|
+
};
|
|
67
|
+
const crashDetails = {};
|
|
68
|
+
for (const r of records) {
|
|
69
|
+
if (r.outcome in byOutcome)
|
|
70
|
+
byOutcome[r.outcome]++;
|
|
71
|
+
if (r.outcome === 'gate-crash') {
|
|
72
|
+
const k = r.detail ?? '(사유 없음)';
|
|
73
|
+
crashDetails[k] = (crashDetails[k] ?? 0) + 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { total: records.length, byOutcome, crashDetails };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/shim/log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type GateFinding, type GuardOptions } from '@vordsec/core';
|
|
2
|
+
import { type Decision, type GateSummary } from './decide.js';
|
|
3
|
+
import { type ShimLogger } from './log.js';
|
|
4
|
+
export interface RunShimInput {
|
|
5
|
+
readonly manager?: string;
|
|
6
|
+
readonly realPath?: string;
|
|
7
|
+
readonly argv: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface RunShimDeps {
|
|
10
|
+
readonly logger?: ShimLogger;
|
|
11
|
+
readonly now?: () => Date;
|
|
12
|
+
readonly guard?: (line: string, opts: GuardOptions) => Promise<{
|
|
13
|
+
stop: boolean;
|
|
14
|
+
notApplicable?: string;
|
|
15
|
+
findings: readonly GateFinding[];
|
|
16
|
+
}>;
|
|
17
|
+
/** 원본 실행. 테스트에서 주입한다 */
|
|
18
|
+
readonly exec?: (real: string, argv: readonly string[]) => number;
|
|
19
|
+
readonly onExit?: (code: number) => void;
|
|
20
|
+
readonly write?: (line: string) => void;
|
|
21
|
+
readonly crashPassesThrough?: boolean;
|
|
22
|
+
readonly cwd?: string;
|
|
23
|
+
}
|
|
24
|
+
export { formatFindings } from '../report/gate.js';
|
|
25
|
+
export declare function toSummary(report: {
|
|
26
|
+
stop: boolean;
|
|
27
|
+
notApplicable?: string;
|
|
28
|
+
findings: readonly GateFinding[];
|
|
29
|
+
}): GateSummary;
|
|
30
|
+
/**
|
|
31
|
+
* 게이트를 돌리고, 통과면 원본을 그대로 실행한다.
|
|
32
|
+
*
|
|
33
|
+
* **크래시를 삼킨다.** 여기서 예외가 새어 나가면 종료코드 127 로 매니저가 통째로 죽는다 —
|
|
34
|
+
* 실측에서 본 그 상태다. 어떤 경우에도 판정은 나오고 기록은 남는다 (ADR-026 제약).
|
|
35
|
+
*/
|
|
36
|
+
export declare function runShim(input: RunShimInput, deps?: RunShimDeps): Promise<Decision>;
|
|
37
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/shim/run.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,WAAW,EAAE,KAAK,YAAY,EACpC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAU,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAIvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC;QAC7D,IAAI,EAAE,OAAO,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;KAClC,CAAC,CAAC;IACH,wBAAwB;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAClC,GAAG,WAAW,CASd;AASD;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2D5F"}
|
package/dist/shim/run.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shim 런타임 — 래퍼 3종이 전부 여기로 모인다 (ADR-026).
|
|
3
|
+
*
|
|
4
|
+
* **이 패키지에서 프로세스를 띄우는 유일한 파일이다.**
|
|
5
|
+
* `child_process` 는 HC-1 정적 층의 금지 목록에 있다 — `curl`·`wget` 으로 네트워크를
|
|
6
|
+
* 우회하는 경로를 막으려고 넣은 것이다. 여기서는 **egress 예외로 처리하지 않는다.**
|
|
7
|
+
* 프로세스 실행은 egress 가 아니므로 별도 층이 이 파일을 좁게 허용하되,
|
|
8
|
+
* **네트워크 모듈과 URL 리터럴은 여전히 금지**한다. `test/no-source-egress.test.ts` 가 강제한다.
|
|
9
|
+
*
|
|
10
|
+
* 실행 대상은 **설치 시점에 박아 둔 원본 경로 하나뿐**이다. 인자를 조립하지 않고 그대로 넘긴다.
|
|
11
|
+
*/
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { guardCommand, stopsInstall, } from '@vordsec/core';
|
|
15
|
+
import { decide } from './decide.js';
|
|
16
|
+
import { fileLogger } from './log.js';
|
|
17
|
+
import { defaultShimDir } from './install.js';
|
|
18
|
+
import { formatFindings } from '../report/gate.js';
|
|
19
|
+
// 판정을 줄로 옮기는 일은 **`report/gate.ts` 가 한다.**
|
|
20
|
+
// shim 과 `guard`·MCP 가 같은 문장을 내야 한다 — 경로마다 다르게 읽히면 다른 제품이다.
|
|
21
|
+
export { formatFindings } from '../report/gate.js';
|
|
22
|
+
export function toSummary(report) {
|
|
23
|
+
const stopping = report.findings.filter((f) => stopsInstall(f.action));
|
|
24
|
+
return {
|
|
25
|
+
stop: report.stop,
|
|
26
|
+
...(report.notApplicable === undefined ? {} : { notApplicable: report.notApplicable }),
|
|
27
|
+
lines: formatFindings(report.findings),
|
|
28
|
+
hasUnverifiable: stopping.length > 0 && stopping.every((f) => f.action === 'unverifiable'),
|
|
29
|
+
packages: [...new Set(report.findings.map((f) => f.packageName))],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function defaultExec(real, argv) {
|
|
33
|
+
// **인자를 재조립하지 않는다.** 문자열로 합치면 따옴표가 깨진다
|
|
34
|
+
const r = spawnSync(real, [...argv], { stdio: 'inherit', shell: false });
|
|
35
|
+
if (r.error !== undefined)
|
|
36
|
+
return 127;
|
|
37
|
+
return r.status ?? 1;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 게이트를 돌리고, 통과면 원본을 그대로 실행한다.
|
|
41
|
+
*
|
|
42
|
+
* **크래시를 삼킨다.** 여기서 예외가 새어 나가면 종료코드 127 로 매니저가 통째로 죽는다 —
|
|
43
|
+
* 실측에서 본 그 상태다. 어떤 경우에도 판정은 나오고 기록은 남는다 (ADR-026 제약).
|
|
44
|
+
*/
|
|
45
|
+
export async function runShim(input, deps = {}) {
|
|
46
|
+
const now = deps.now ?? (() => new Date());
|
|
47
|
+
const write = deps.write ?? ((l) => process.stderr.write(l + '\n'));
|
|
48
|
+
const onExit = deps.onExit ?? ((c) => process.exit(c));
|
|
49
|
+
const exec = deps.exec ?? defaultExec;
|
|
50
|
+
const logger = deps.logger ?? fileLogger(path.dirname(defaultShimDir()));
|
|
51
|
+
const manager = input.manager ?? '';
|
|
52
|
+
const real = input.realPath ?? '';
|
|
53
|
+
const started = Date.now();
|
|
54
|
+
let decision;
|
|
55
|
+
try {
|
|
56
|
+
if (manager === '' || real === '')
|
|
57
|
+
throw new Error('shim 인자가 비었다');
|
|
58
|
+
const guard = deps.guard ?? ((line, opts) => guardCommand(line, opts));
|
|
59
|
+
const report = await guard([manager, ...input.argv].join(' '), deps.cwd === undefined ? {} : { cwd: deps.cwd });
|
|
60
|
+
decision = decide({
|
|
61
|
+
manager,
|
|
62
|
+
gate: toSummary(report),
|
|
63
|
+
...(deps.crashPassesThrough === undefined
|
|
64
|
+
? {} : { crashPassesThrough: deps.crashPassesThrough }),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
decision = decide({
|
|
69
|
+
manager,
|
|
70
|
+
crash: e instanceof Error ? `${e.name}: ${e.message}` : String(e),
|
|
71
|
+
...(deps.crashPassesThrough === undefined
|
|
72
|
+
? {} : { crashPassesThrough: deps.crashPassesThrough }),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
logger.append({
|
|
76
|
+
at: now().toISOString(),
|
|
77
|
+
manager,
|
|
78
|
+
outcome: decision.outcome,
|
|
79
|
+
packages: decision.packages,
|
|
80
|
+
...(decision.detail === undefined ? {} : { detail: decision.detail }),
|
|
81
|
+
ms: Date.now() - started,
|
|
82
|
+
});
|
|
83
|
+
for (const line of decision.stderr)
|
|
84
|
+
write(line);
|
|
85
|
+
if (!decision.runReal) {
|
|
86
|
+
onExit(decision.exitCode ?? 1);
|
|
87
|
+
return decision;
|
|
88
|
+
}
|
|
89
|
+
// ── 통과. **종료 코드를 그대로 전달한다** — 충실도가 깨지면 스크립트가 잘못 동작한다
|
|
90
|
+
let code = 0;
|
|
91
|
+
try {
|
|
92
|
+
code = exec(real, input.argv);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
code = 127;
|
|
96
|
+
}
|
|
97
|
+
onExit(code);
|
|
98
|
+
return decision;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/shim/run.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vordsec/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI 에이전트의 위험한 패키지 설치를 차단하고, 침해로 이어지는 설정을 로컬에서만 스캔한다.",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/createrman343212/Vord.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/createrman343212/Vord#readme",
|
|
12
|
+
"bugs": "https://github.com/createrman343212/Vord/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"security",
|
|
15
|
+
"supply-chain",
|
|
16
|
+
"slopsquatting",
|
|
17
|
+
"ai-agent",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"mcp",
|
|
20
|
+
"sarif",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"vord": "./dist/bin.js"
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@vordsec/core": "0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc --build"
|
|
43
|
+
}
|
|
44
|
+
}
|