deel-local-cli 1.19.2 → 1.19.4
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/README.ko.md +4 -2
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/safety/secrets.js +106 -4
package/README.ko.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/ko/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](package.json)
|
|
@@ -301,7 +301,7 @@ deel --offline
|
|
|
301
301
|
무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
|
|
302
302
|
|
|
303
303
|
```
|
|
304
|
-
deel 1.19.
|
|
304
|
+
deel 1.19.4 ⌂ 이 안
|
|
305
305
|
보냄 이 컴퓨터 안 127.0.0.1:11434 ← 여기 말고는 어디로도 안 갑니다
|
|
306
306
|
```
|
|
307
307
|
|
|
@@ -1289,6 +1289,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
1289
1289
|
|
|
1290
1290
|
| 판 | 무엇이 바뀌었나 |
|
|
1291
1291
|
|---|---|
|
|
1292
|
+
| [1.19.4](docs/ko/releases/1.19.md#1194) | 주석을 달아 둔 자리에서 비밀이 또 샜습니다 |
|
|
1293
|
+
| [1.19.3](docs/ko/releases/1.19.md#1193) | YAML 에 적은 비밀이 평문으로 나가고 있었습니다 |
|
|
1292
1294
|
| [1.19.2](docs/ko/releases/1.19.md#1192) | 울타리를 옮긴 자리에 틈이 생겼습니다 — 1.19.1 이 낸 구멍을 막습니다 |
|
|
1293
1295
|
| [1.19.1](docs/ko/releases/1.19.md#1191) | 열두 번째 눈이 본 것, 그리고 오탐 열넷 — 지적 27건 중 13건이 진짜였습니다 |
|
|
1294
1296
|
| [1.19.0](docs/ko/releases/1.19.md#1190) | 한쪽을 맞히면 반대쪽이 틀렸습니다 — 검사 9,093개로 양쪽을 같이 잽니다 |
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Vendor APIs connect too — **only when you say so**
|
|
|
20
20
|
|
|
21
21
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/en/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](package.json)
|
|
@@ -374,7 +374,7 @@ deel --offline
|
|
|
374
374
|
The destination is printed at the top of every session:
|
|
375
375
|
|
|
376
376
|
```
|
|
377
|
-
deel 1.19.
|
|
377
|
+
deel 1.19.4 ⌂ inside
|
|
378
378
|
Sends to this machine 127.0.0.1:11434 ← nowhere else
|
|
379
379
|
```
|
|
380
380
|
|
|
@@ -1350,6 +1350,8 @@ so one run tells you everything.
|
|
|
1350
1350
|
|
|
1351
1351
|
| Version | What changed |
|
|
1352
1352
|
|---|---|
|
|
1353
|
+
| [1.19.4](docs/en/releases/1.19.md#1194) | Secrets leaked again wherever a comment was written |
|
|
1354
|
+
| [1.19.3](docs/en/releases/1.19.md#1193) | Secrets written in YAML were going out in plaintext |
|
|
1353
1355
|
| [1.19.2](docs/en/releases/1.19.md#1192) | A gap appeared where the fence was moved — closing the hole 1.19.1 opened |
|
|
1354
1356
|
| [1.19.1](docs/en/releases/1.19.md#1191) | What the twelfth pair of eyes found, and the fourteen false alarms — 13 of 27 findings survived |
|
|
1355
1357
|
| [1.19.0](docs/en/releases/1.19.md#1190) | Getting one side right broke the other — 9,093 checks that measure both sides |
|
package/package.json
CHANGED
package/src/safety/secrets.js
CHANGED
|
@@ -43,6 +43,104 @@ const 표 = (종류) => `«가림:${종류}»`;
|
|
|
43
43
|
* 넓게 잡지 않는다. 코드에 흔한 글자를 비밀로 오인해 가려 버리면, 모델이 보는
|
|
44
44
|
* 코드가 조용히 달라진다. 그건 여기서 막으려는 것보다 더 나쁜 고장이다.
|
|
45
45
|
*/
|
|
46
|
+
/**
|
|
47
|
+
* 한 줄 끝에 붙은 `#` 주석.
|
|
48
|
+
*
|
|
49
|
+
* 주석 안은 코드가 아니다. 여기를 떼지 않으면 `# https://api.example.com` 의
|
|
50
|
+
* `//`, `# (운영환경용)` 의 괄호, `# type: bearer` 의 `type` 이 전부 「이 줄은
|
|
51
|
+
* 코드」 라는 증거로 읽혀 **그 줄의 비밀이 평문으로 나간다**(14차 리뷰).
|
|
52
|
+
*/
|
|
53
|
+
const 주석 = /(?:^|[ \t])#.*$/;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `.env` · YAML 한 줄에는 없고 코드 한 줄에는 있는 것들.
|
|
57
|
+
*
|
|
58
|
+
* 넣지 않은 것들에도 까닭이 있다.
|
|
59
|
+
*
|
|
60
|
+
* - `export` — `.env` 도 `export FOO=bar` 를 쓴다.
|
|
61
|
+
* - `{}` — YAML 흐름 표기(`{ API_KEY: 값 }`)가 문법상 중괄호를 쓴다.
|
|
62
|
+
* 중괄호를 증거로 삼으면 그 한 줄이 통째로 샜다.
|
|
63
|
+
* - 쌍반점은 그냥 증거로 둔다. `.env` 값 끝에 쌍반점이 붙어 오면
|
|
64
|
+
* (`API_KEY=prod.v1.secret;`) 그 줄은 샌다. 그런데 그것과 한 줄짜리
|
|
65
|
+
* 타입 표기(`API_KEY: types.ApiKey;`)는 **글자로 구별이 안 된다** —
|
|
66
|
+
* 둘 다 점으로 이은 값 뒤에 쌍반점 하나다. 뒤에 무언가 더 있는
|
|
67
|
+
* 쌍반점만 보게 좁혀 봤더니 그 타입 표기가 뭉개졌다(검사가 잡았다).
|
|
68
|
+
*
|
|
69
|
+
* 줄 끝 `,` 는 거꾸로 코드의 표시다. YAML 블록 매핑은 줄 끝에 쉼표를 안 찍고,
|
|
70
|
+
* 여러 줄 객체 리터럴은 찍는다.
|
|
71
|
+
*/
|
|
72
|
+
const 코드표시 = /[()]|=>|\/\/|\/\*|;|,[ \t]*$|\b(?:const|let|var|interface|type|class|enum|function|def|readonly|declare|public|private|protected|static|final)\b/;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 점으로 이은 값이 **비밀인가 코드가 가리키는 이름인가**.
|
|
76
|
+
*
|
|
77
|
+
* ── 빈칸은 가르는 신호가 아니었다 (13차 리뷰) ──────────────────────────
|
|
78
|
+
*
|
|
79
|
+
* 여태 「값 앞에 빈칸이 있으면 코드」 로 갈랐다. `.env` 는 `NAME=값` 으로
|
|
80
|
+
* 붙여 쓰니 빈칸이 있으면 코드라는 셈이었다. 재 보니 **넷 중 하나만**
|
|
81
|
+
* 맞혔다.
|
|
82
|
+
*
|
|
83
|
+
* API_KEY=prod.v1.secret 가림 맞음
|
|
84
|
+
* API_KEY: token.v1.secret (YAML) 그대로 비밀이 평문으로 남음
|
|
85
|
+
* API_KEY= prod.v1.secret (.env) 그대로 같은 자리
|
|
86
|
+
* interface Cfg { API_KEY:types.ApiKey; } 가림 코드를 망가뜨림
|
|
87
|
+
*
|
|
88
|
+
* YAML 은 문법이 쌍점 뒤 빈칸을 **요구한다.** 그러니 빈칸을 코드의 표시로
|
|
89
|
+
* 읽는 한 YAML 에 적힌 비밀은 언제까지고 샌다. 그리고 붙여 쓴 타입
|
|
90
|
+
* 표기(`API_KEY:types.ApiKey`)는 거꾸로 코드인데 가려졌다.
|
|
91
|
+
*
|
|
92
|
+
* ── 그 줄이 코드인가만 본다 ───────────────────────────────────────────
|
|
93
|
+
*
|
|
94
|
+
* `.env` 와 YAML 한 줄에는 이름과 값밖에 없다. `주석` 을 떼고 남은 자리에
|
|
95
|
+
* `코드표시` 가 하나라도 있으면 코드다.
|
|
96
|
+
*
|
|
97
|
+
* ── 값 모양으로 가르려다 물러섰다 ─────────────────────────────────────
|
|
98
|
+
*
|
|
99
|
+
* 처음엔 값도 같이 봤다 — 「숫자가 끼거나 마디가 길면 비밀」. 그러면
|
|
100
|
+
* `config.api_key` 를 놔두면서 `token.v1.secret` 을 가릴 수 있다. 그런데
|
|
101
|
+
* 이 저장소에 이미 박혀 있는 검사가 **`API_KEY=my.secret.value` 를 가리라고**
|
|
102
|
+
* 한다. 그것과 `config.api_key` 는 문법으로 구별이 안 된다 — 둘 다 짧은
|
|
103
|
+
* 낱말을 점으로 이은 꼴이다. 그러니 값 모양으로 가를 수 있다는 생각을
|
|
104
|
+
* 버린다. 갈리지 않는 것을 갈리는 척하면 그게 더 위험하다.
|
|
105
|
+
*
|
|
106
|
+
* 점이 없는 맨 값(`const API_KEY = apiKey;`)까지 줄을 묻게 넓혀 봤더니,
|
|
107
|
+
* 이 저장소가 **가리라고 못 박아 둔** `const API_KEY = ENV-PROD-SECRET-KEY-12345;`
|
|
108
|
+
* 와 `call(API_KEY = abcd1234)` 가 같이 풀렸다. 점 없는 한 낱말은 이름씨일
|
|
109
|
+
* 수도 비밀일 수도 있고, 그 둘은 여기서 갈리지 않는다. 그래서 점이 있는
|
|
110
|
+
* 값만 줄을 묻는다 — 뭉개지는 쪽이 아니라 가리는 쪽으로 남긴다.
|
|
111
|
+
*
|
|
112
|
+
* ── 그래서 치르는 값 ──────────────────────────────────────────────────
|
|
113
|
+
*
|
|
114
|
+
* 한 줄만 보니, 한 줄에 표시가 없는 코드는 설정 줄과 똑같이 생긴다.
|
|
115
|
+
*
|
|
116
|
+
* API_KEY = config.api_key 파이썬 대입인데 가려진다
|
|
117
|
+
* { API_KEY: config.apiKey } 한 줄 물건인데 가려진다
|
|
118
|
+
* interface Cfg {
|
|
119
|
+
* API_KEY: types.ApiKey 쌍반점을 생략하면 가려진다
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* 이건 알고 받는 값이다. 줄이 망가지는 것보다 **비밀이 나가는 것이 훨씬
|
|
123
|
+
* 나쁘다**(1.19.0 에 적어 둔 대로). 가려도 줄은 읽힌다 —
|
|
124
|
+
* `API_KEY = «가림:환경변수»` 로 남고, 무엇이 있었는지도 보인다.
|
|
125
|
+
*
|
|
126
|
+
* 위 칸을 메우려면 앞뒤 줄을 봐야 하는데, 그 자리에 갈릴 것이 없다 —
|
|
127
|
+
* 들여 쓴 `API_KEY: 값` 은 YAML 겹 매핑이면 비밀이고 객체 리터럴이면
|
|
128
|
+
* 코드인데, 두 줄이 글자 하나 안 다르다. 겹 매핑이 훨씬 흔하므로
|
|
129
|
+
* **가리는 쪽으로 둔다.**
|
|
130
|
+
*
|
|
131
|
+
* @param {string} 전체 가리는 글 통째로
|
|
132
|
+
* @param {number} 자리 이 짝이 시작한 자리
|
|
133
|
+
* @param {string} 값 점으로 이어졌을 수 있는 값
|
|
134
|
+
* @returns {boolean} 가릴까
|
|
135
|
+
*/
|
|
136
|
+
const 점값을가릴까 = (전체, 자리, 값) => {
|
|
137
|
+
if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(값)) return true;
|
|
138
|
+
const 앞 = 전체.lastIndexOf('\n', 자리) + 1;
|
|
139
|
+
const 뒤 = 전체.indexOf('\n', 자리);
|
|
140
|
+
const 줄 = 전체.slice(앞, 뒤 < 0 ? 전체.length : 뒤).replace(주석, '');
|
|
141
|
+
return !코드표시.test(줄);
|
|
142
|
+
};
|
|
143
|
+
|
|
46
144
|
export const 갈래 = [
|
|
47
145
|
{
|
|
48
146
|
id: '사설키',
|
|
@@ -380,11 +478,15 @@ export const 갈래 = [
|
|
|
380
478
|
*/
|
|
381
479
|
// 값 자리가 또 환경변수를 읽는 것이면 참조다 — Node · Vite · 파이썬 · Deno.
|
|
382
480
|
id: '환경변수',
|
|
383
|
-
re: /\b([A-Z][A-Z0-9_]{2,}(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIALS?))(?:(["']?[ \t]*(?::[^\n,;]{0,200}?|:[^\n]{0,200}?[>\])}][^\n,;]{0,80}?)[ \t]*=(?!=|>)\s*)((?:\(\s*)+)?(["'`])(?!«)((?:\\[^\n]|(?!\4)[^\n]){4,}?)\4((?:\s*\))+)?|(["']?\s*(?::(?![^\n]{0,200}?=[ \t]*«가림:)|=)\s*)((?:\(\s*)+)?(?:(["'`])(?!«)((?:\\[^\n]|(?!\9)[^\n]){4,}?)\9|(?!«)(?![\w$.]+\s*[([])(?!(?:string|number|boolean|bigint|symbol|object|unknown|never|void|null|undefined|true|false)\b)(
|
|
481
|
+
re: /\b([A-Z][A-Z0-9_]{2,}(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIALS?))(?:(["']?[ \t]*(?::[^\n,;]{0,200}?|:[^\n]{0,200}?[>\])}][^\n,;]{0,80}?)[ \t]*=(?!=|>)\s*)((?:\(\s*)+)?(["'`])(?!«)((?:\\[^\n]|(?!\4)[^\n]){4,}?)\4((?:\s*\))+)?|(["']?\s*(?::(?![^\n]{0,200}?=[ \t]*«가림:)|=)\s*)((?:\(\s*)+)?(?:(["'`])(?!«)((?:\\[^\n]|(?!\9)[^\n]){4,}?)\9|(?!«)(?![\w$.]+\s*[([])(?!(?:string|number|boolean|bigint|symbol|object|unknown|never|void|null|undefined|true|false)\b)(?![^\n]{0,200}?}[ \t]*(?:=(?!=|>)|\)))(?!\(*\s*(?:process\.env|import\.meta\.env|globalThis\.process\.env|globalThis\.env|os\.environ|os\.getenv|ENV(?=\s*[[.(])|Deno\.env|Bun\.env)\b)([^\s"'()[\]{},;<>]{4,})(?=[ \t\r]*(?:[;,)\]}\n#]|$)))((?:\s*\))+)?)/g,
|
|
384
482
|
바꾸기: (m, 이름, 타입사이, 타입여는, 따A, 값A, 타입닫는,
|
|
385
|
-
민사이, 여는, 따B, 값B, 값C,
|
|
386
|
-
|
|
387
|
-
|
|
483
|
+
민사이, 여는, 따B, 값B, 값C, 닫는, 자리, 전체) => {
|
|
484
|
+
// 따옴표 없는 맨 값만 「코드가 가리키는 이름인가」 를 되묻는다.
|
|
485
|
+
if (값C !== undefined && !점값을가릴까(전체, 자리, 값C)) return m;
|
|
486
|
+
return 타입사이 === undefined
|
|
487
|
+
? `${이름}${민사이}${여는 ?? ''}${표('환경변수')}${닫는 ?? ''}`
|
|
488
|
+
: `${이름}${타입사이}${타입여는 ?? ''}${표('환경변수')}${타입닫는 ?? ''}`;
|
|
489
|
+
},
|
|
388
490
|
},
|
|
389
491
|
];
|
|
390
492
|
|