@uniglot/wont-let-you-see 0.1.1 → 0.2.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/.github/workflows/ci.yml +14 -0
- package/.github/workflows/publish.yml +20 -0
- package/.github/workflows/release.yml +20 -0
- package/.github/workflows/test.yml +13 -0
- package/README.md +6 -0
- package/dist/index.js +24 -10
- package/package.json +1 -1
- package/src/__tests__/config.test.ts +24 -20
- package/src/__tests__/patterns.test.ts +98 -23
- package/src/config.ts +27 -8
- package/src/masker.ts +6 -0
- package/src/patterns.ts +7 -7
- package/dist/patterns.js +0 -5233
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [main]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
check:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: oven-sh/setup-bun@v2
|
|
12
|
+
- run: bun install --frozen-lockfile
|
|
13
|
+
- run: bun run typecheck
|
|
14
|
+
- run: bunx prettier --check "src/**/*.ts"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
publish:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: oven-sh/setup-bun@v2
|
|
12
|
+
- uses: actions/setup-node@v4
|
|
13
|
+
with:
|
|
14
|
+
registry-url: https://registry.npmjs.org
|
|
15
|
+
- run: bun install --frozen-lockfile
|
|
16
|
+
- run: bun run build
|
|
17
|
+
- run: bunx tsc --emitDeclarationOnly --declaration --outDir dist
|
|
18
|
+
- run: npm publish
|
|
19
|
+
env:
|
|
20
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Draft Release
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
release:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- id: pkg
|
|
14
|
+
run: echo "version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
|
|
15
|
+
- uses: softprops/action-gh-release@v2
|
|
16
|
+
with:
|
|
17
|
+
tag_name: v${{ steps.pkg.outputs.version }}
|
|
18
|
+
name: v${{ steps.pkg.outputs.version }}
|
|
19
|
+
draft: true
|
|
20
|
+
generate_release_notes: true
|
package/README.md
CHANGED
|
@@ -92,6 +92,12 @@ The plugin automatically masks output from:
|
|
|
92
92
|
| `ebs` | EBS Volume IDs | `vol-0123456789abcdef0` |
|
|
93
93
|
| `snapshot` | EBS Snapshot IDs | `snap-0123456789abcdef0` |
|
|
94
94
|
| `eni` | Network Interface IDs | `eni-0123456789abcdef0` |
|
|
95
|
+
| `vpc-endpoint` | VPC Endpoint IDs | `vpce-0123456789abcdef0` |
|
|
96
|
+
| `transit-gateway` | Transit Gateway IDs | `tgw-0123456789abcdef0` |
|
|
97
|
+
| `customer-gateway` | Customer Gateway IDs | `cgw-0123456789abcdef0` |
|
|
98
|
+
| `vpn-gateway` | VPN Gateway IDs | `vgw-0123456789abcdef0` |
|
|
99
|
+
| `vpn-connection` | VPN Connection IDs | `vpn-0123456789abcdef0` |
|
|
100
|
+
| `ecr-repo` | ECR Repository URIs | `123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repo` |
|
|
95
101
|
|
|
96
102
|
### Kubernetes Resources (EKS)
|
|
97
103
|
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,13 @@ var AWS_PATTERNS = {
|
|
|
14
14
|
ebs: /^vol-[0-9a-f]{8,17}$/,
|
|
15
15
|
snapshot: /^snap-[0-9a-f]{8,17}$/,
|
|
16
16
|
eni: /^eni-[0-9a-f]{8,17}$/,
|
|
17
|
+
vpcEndpoint: /^vpce-[0-9a-f]{8,17}$/,
|
|
18
|
+
transitGateway: /^tgw-[0-9a-f]{8,17}$/,
|
|
19
|
+
customerGateway: /^cgw-[0-9a-f]{8,17}$/,
|
|
20
|
+
vpnGateway: /^vgw-[0-9a-f]{8,17}$/,
|
|
21
|
+
vpnConnection: /^vpn-[0-9a-f]{8,17}$/,
|
|
17
22
|
accountId: /"(?:OwnerId|AccountId|Owner|account_id)":\s*"(\d{12})"/,
|
|
23
|
+
ecrRepoUri: /^(?:\d{12}|#\(custom-\d+\))\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com\/[a-z0-9._\/-]+$/,
|
|
18
24
|
accessKeyId: /(?:^|[^A-Z0-9])(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}(?:[^A-Z0-9]|$)/
|
|
19
25
|
};
|
|
20
26
|
var K8S_PATTERNS = {
|
|
@@ -28,11 +34,6 @@ var COMMON_PATTERNS = {
|
|
|
28
34
|
privateKey: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
29
35
|
apiKeyField: /"(?:api_key|apiKey|secret_key|secretKey|access_token|auth_token|password|token)":\s*"([^"]+)"/
|
|
30
36
|
};
|
|
31
|
-
var PATTERNS = {
|
|
32
|
-
...AWS_PATTERNS,
|
|
33
|
-
...K8S_PATTERNS,
|
|
34
|
-
...COMMON_PATTERNS
|
|
35
|
-
};
|
|
36
37
|
|
|
37
38
|
// src/mapping.ts
|
|
38
39
|
import {
|
|
@@ -113,9 +114,16 @@ function getOriginal(sessionId, token) {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
// src/config.ts
|
|
116
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
existsSync as nodeExistsSync,
|
|
119
|
+
readFileSync as nodeReadFileSync
|
|
120
|
+
} from "fs";
|
|
117
121
|
import { join as join2, dirname } from "path";
|
|
118
122
|
import { homedir as homedir2 } from "os";
|
|
123
|
+
var fsAdapter = {
|
|
124
|
+
existsSync: nodeExistsSync,
|
|
125
|
+
readFileSync: nodeReadFileSync
|
|
126
|
+
};
|
|
119
127
|
var DEFAULT_CONFIG = {
|
|
120
128
|
enabled: true,
|
|
121
129
|
revealedPatterns: [],
|
|
@@ -127,7 +135,7 @@ function findConfigInAncestors(startDir) {
|
|
|
127
135
|
let currentDir = startDir;
|
|
128
136
|
while (true) {
|
|
129
137
|
const configPath = join2(currentDir, CONFIG_FILENAME);
|
|
130
|
-
if (
|
|
138
|
+
if (fsAdapter.existsSync(configPath)) {
|
|
131
139
|
return configPath;
|
|
132
140
|
}
|
|
133
141
|
if (currentDir === home) {
|
|
@@ -145,14 +153,14 @@ function loadJsonConfig() {
|
|
|
145
153
|
const ancestorConfig = findConfigInAncestors(process.cwd());
|
|
146
154
|
if (ancestorConfig) {
|
|
147
155
|
try {
|
|
148
|
-
const content =
|
|
156
|
+
const content = fsAdapter.readFileSync(ancestorConfig, "utf-8");
|
|
149
157
|
return JSON.parse(content);
|
|
150
158
|
} catch {}
|
|
151
159
|
}
|
|
152
160
|
const homeConfig = join2(homedir2(), CONFIG_FILENAME);
|
|
153
|
-
if (
|
|
161
|
+
if (fsAdapter.existsSync(homeConfig)) {
|
|
154
162
|
try {
|
|
155
|
-
const content =
|
|
163
|
+
const content = fsAdapter.readFileSync(homeConfig, "utf-8");
|
|
156
164
|
return JSON.parse(content);
|
|
157
165
|
} catch {}
|
|
158
166
|
}
|
|
@@ -210,6 +218,12 @@ var PATTERN_ORDER = [
|
|
|
210
218
|
{ pattern: AWS_PATTERNS.natGateway, type: "nat-gateway" },
|
|
211
219
|
{ pattern: AWS_PATTERNS.networkAcl, type: "network-acl" },
|
|
212
220
|
{ pattern: AWS_PATTERNS.eni, type: "eni" },
|
|
221
|
+
{ pattern: AWS_PATTERNS.vpcEndpoint, type: "vpc-endpoint" },
|
|
222
|
+
{ pattern: AWS_PATTERNS.transitGateway, type: "transit-gateway" },
|
|
223
|
+
{ pattern: AWS_PATTERNS.customerGateway, type: "customer-gateway" },
|
|
224
|
+
{ pattern: AWS_PATTERNS.vpnGateway, type: "vpn-gateway" },
|
|
225
|
+
{ pattern: AWS_PATTERNS.vpnConnection, type: "vpn-connection" },
|
|
226
|
+
{ pattern: AWS_PATTERNS.ecrRepoUri, type: "ecr-repo" },
|
|
213
227
|
{ pattern: AWS_PATTERNS.ami, type: "ami" },
|
|
214
228
|
{ pattern: AWS_PATTERNS.ec2Instance, type: "ec2-instance" },
|
|
215
229
|
{ pattern: AWS_PATTERNS.ebs, type: "ebs" },
|
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
getConfig,
|
|
4
|
+
resetConfig,
|
|
5
|
+
isPatternEnabled,
|
|
6
|
+
setFsAdapter,
|
|
7
|
+
resetFsAdapter,
|
|
8
|
+
} from "../config";
|
|
4
9
|
import * as path from "path";
|
|
5
10
|
|
|
6
|
-
vi.
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
...actual,
|
|
10
|
-
existsSync: vi.fn(),
|
|
11
|
-
readFileSync: vi.fn(),
|
|
12
|
-
};
|
|
13
|
-
});
|
|
11
|
+
const mockExistsSync = vi.fn();
|
|
12
|
+
const mockReadFileSync = vi.fn();
|
|
14
13
|
|
|
15
14
|
describe("Config", () => {
|
|
16
15
|
const originalEnv = { ...process.env };
|
|
@@ -18,14 +17,19 @@ describe("Config", () => {
|
|
|
18
17
|
|
|
19
18
|
beforeEach(() => {
|
|
20
19
|
resetConfig();
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
setFsAdapter({
|
|
21
|
+
existsSync: mockExistsSync,
|
|
22
|
+
readFileSync: mockReadFileSync,
|
|
23
|
+
});
|
|
24
|
+
mockExistsSync.mockReturnValue(false);
|
|
25
|
+
mockReadFileSync.mockReturnValue("{}");
|
|
23
26
|
});
|
|
24
27
|
|
|
25
28
|
afterEach(() => {
|
|
26
29
|
process.env = { ...originalEnv };
|
|
27
30
|
process.cwd = originalCwd;
|
|
28
31
|
resetConfig();
|
|
32
|
+
resetFsAdapter();
|
|
29
33
|
vi.clearAllMocks();
|
|
30
34
|
});
|
|
31
35
|
|
|
@@ -116,10 +120,10 @@ describe("Config", () => {
|
|
|
116
120
|
it("should find config in parent directory when cwd is subdirectory", () => {
|
|
117
121
|
process.cwd = () => "/project/packages/app";
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
mockExistsSync.mockImplementation((p: string) => {
|
|
120
124
|
return p === path.join("/project", ".wont-let-you-see.json");
|
|
121
125
|
});
|
|
122
|
-
|
|
126
|
+
mockReadFileSync.mockReturnValue(
|
|
123
127
|
JSON.stringify({ enabled: false, customPatterns: ["secret123"] }),
|
|
124
128
|
);
|
|
125
129
|
|
|
@@ -131,10 +135,10 @@ describe("Config", () => {
|
|
|
131
135
|
it("should find config in grandparent directory", () => {
|
|
132
136
|
process.cwd = () => "/project/packages/app/src/components";
|
|
133
137
|
|
|
134
|
-
|
|
138
|
+
mockExistsSync.mockImplementation((p: string) => {
|
|
135
139
|
return p === path.join("/project", ".wont-let-you-see.json");
|
|
136
140
|
});
|
|
137
|
-
|
|
141
|
+
mockReadFileSync.mockReturnValue(
|
|
138
142
|
JSON.stringify({ revealedPatterns: ["ipv4"] }),
|
|
139
143
|
);
|
|
140
144
|
|
|
@@ -145,13 +149,13 @@ describe("Config", () => {
|
|
|
145
149
|
it("should prefer config in closer ancestor over distant ancestor", () => {
|
|
146
150
|
process.cwd = () => "/project/packages/app";
|
|
147
151
|
|
|
148
|
-
|
|
152
|
+
mockExistsSync.mockImplementation((p: string) => {
|
|
149
153
|
return (
|
|
150
154
|
p === path.join("/project/packages/app", ".wont-let-you-see.json") ||
|
|
151
155
|
p === path.join("/project", ".wont-let-you-see.json")
|
|
152
156
|
);
|
|
153
157
|
});
|
|
154
|
-
|
|
158
|
+
mockReadFileSync.mockImplementation((p: string) => {
|
|
155
159
|
if (
|
|
156
160
|
p === path.join("/project/packages/app", ".wont-let-you-see.json")
|
|
157
161
|
) {
|
|
@@ -167,13 +171,13 @@ describe("Config", () => {
|
|
|
167
171
|
it("should prefer cwd config over parent config", () => {
|
|
168
172
|
process.cwd = () => "/project/subdir";
|
|
169
173
|
|
|
170
|
-
|
|
174
|
+
mockExistsSync.mockImplementation((p: string) => {
|
|
171
175
|
return (
|
|
172
176
|
p === path.join("/project/subdir", ".wont-let-you-see.json") ||
|
|
173
177
|
p === path.join("/project", ".wont-let-you-see.json")
|
|
174
178
|
);
|
|
175
179
|
});
|
|
176
|
-
|
|
180
|
+
mockReadFileSync.mockImplementation((p: string) => {
|
|
177
181
|
if (p === path.join("/project/subdir", ".wont-let-you-see.json")) {
|
|
178
182
|
return JSON.stringify({ enabled: false });
|
|
179
183
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
AWS_PATTERNS,
|
|
4
|
-
K8S_PATTERNS,
|
|
5
|
-
COMMON_PATTERNS,
|
|
6
|
-
PATTERNS,
|
|
7
|
-
} from "../patterns";
|
|
2
|
+
import { AWS_PATTERNS, K8S_PATTERNS, COMMON_PATTERNS } from "../patterns";
|
|
8
3
|
|
|
9
4
|
describe("AWS_PATTERNS", () => {
|
|
10
5
|
describe("ARN", () => {
|
|
@@ -65,6 +60,96 @@ describe("AWS_PATTERNS", () => {
|
|
|
65
60
|
});
|
|
66
61
|
});
|
|
67
62
|
|
|
63
|
+
describe("VPC networking resources", () => {
|
|
64
|
+
it("should match vpc endpoint", () => {
|
|
65
|
+
expect(AWS_PATTERNS.vpcEndpoint.test("vpce-0123456789abcdef0")).toBe(
|
|
66
|
+
true,
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should match transit gateway", () => {
|
|
71
|
+
expect(AWS_PATTERNS.transitGateway.test("tgw-0123456789abcdef0")).toBe(
|
|
72
|
+
true,
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should match customer gateway", () => {
|
|
77
|
+
expect(AWS_PATTERNS.customerGateway.test("cgw-0123456789abcdef0")).toBe(
|
|
78
|
+
true,
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should match vpn gateway", () => {
|
|
83
|
+
expect(AWS_PATTERNS.vpnGateway.test("vgw-0123456789abcdef0")).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should match vpn connection", () => {
|
|
87
|
+
expect(AWS_PATTERNS.vpnConnection.test("vpn-0123456789abcdef0")).toBe(
|
|
88
|
+
true,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should NOT match invalid vpc endpoint", () => {
|
|
93
|
+
expect(AWS_PATTERNS.vpcEndpoint.test("vpce-invalid")).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("ECR resources", () => {
|
|
98
|
+
it("should match ECR repo URI", () => {
|
|
99
|
+
expect(
|
|
100
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
101
|
+
"123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repo",
|
|
102
|
+
),
|
|
103
|
+
).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should match ECR repo URI with nested path", () => {
|
|
107
|
+
expect(
|
|
108
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
109
|
+
"123456789012.dkr.ecr.eu-central-1.amazonaws.com/org/app/service",
|
|
110
|
+
),
|
|
111
|
+
).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should match ECR repo URI with dots and underscores", () => {
|
|
115
|
+
expect(
|
|
116
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
117
|
+
"123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my_repo.name",
|
|
118
|
+
),
|
|
119
|
+
).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should NOT match invalid ECR URI (wrong account ID length)", () => {
|
|
123
|
+
expect(
|
|
124
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
125
|
+
"12345.dkr.ecr.us-west-2.amazonaws.com/my-repo",
|
|
126
|
+
),
|
|
127
|
+
).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should NOT match non-ECR docker registry", () => {
|
|
131
|
+
expect(AWS_PATTERNS.ecrRepoUri.test("docker.io/library/nginx")).toBe(
|
|
132
|
+
false,
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should match ECR repo URI with masked account ID", () => {
|
|
137
|
+
expect(
|
|
138
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
139
|
+
"#(custom-1).dkr.ecr.us-west-2.amazonaws.com/my-repo",
|
|
140
|
+
),
|
|
141
|
+
).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should match ECR repo URI with masked account ID (higher number)", () => {
|
|
145
|
+
expect(
|
|
146
|
+
AWS_PATTERNS.ecrRepoUri.test(
|
|
147
|
+
"#(custom-123).dkr.ecr.us-east-1.amazonaws.com/app/service",
|
|
148
|
+
),
|
|
149
|
+
).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
68
153
|
describe("Account ID (contextual)", () => {
|
|
69
154
|
it("should match OwnerId field", () => {
|
|
70
155
|
expect(AWS_PATTERNS.accountId.test('"OwnerId": "123456789012"')).toBe(
|
|
@@ -188,28 +273,18 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC
|
|
|
188
273
|
});
|
|
189
274
|
});
|
|
190
275
|
|
|
191
|
-
describe("PATTERNS (combined)", () => {
|
|
192
|
-
it("should include all AWS patterns", () => {
|
|
193
|
-
expect(PATTERNS.vpc).toBe(AWS_PATTERNS.vpc);
|
|
194
|
-
expect(PATTERNS.arn).toBe(AWS_PATTERNS.arn);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("should include all K8S patterns", () => {
|
|
198
|
-
expect(PATTERNS.serviceAccountToken).toBe(K8S_PATTERNS.serviceAccountToken);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("should include all common patterns", () => {
|
|
202
|
-
expect(PATTERNS.ipv4).toBe(COMMON_PATTERNS.ipv4);
|
|
203
|
-
expect(PATTERNS.privateKey).toBe(COMMON_PATTERNS.privateKey);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
276
|
describe("ReDoS safety", () => {
|
|
208
277
|
it("should handle pathological input quickly", () => {
|
|
209
278
|
const pathological = "a".repeat(1000) + "b";
|
|
210
279
|
const start = performance.now();
|
|
211
280
|
|
|
212
|
-
|
|
281
|
+
const allPatterns = [
|
|
282
|
+
...Object.values(AWS_PATTERNS),
|
|
283
|
+
...Object.values(K8S_PATTERNS),
|
|
284
|
+
...Object.values(COMMON_PATTERNS),
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
for (const pattern of allPatterns) {
|
|
213
288
|
pattern.test(pathological);
|
|
214
289
|
}
|
|
215
290
|
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
existsSync as nodeExistsSync,
|
|
3
|
+
readFileSync as nodeReadFileSync,
|
|
4
|
+
} from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
3
6
|
import { homedir } from "os";
|
|
4
7
|
|
|
5
8
|
export interface Config {
|
|
@@ -8,6 +11,24 @@ export interface Config {
|
|
|
8
11
|
customPatterns: string[];
|
|
9
12
|
}
|
|
10
13
|
|
|
14
|
+
interface FsAdapter {
|
|
15
|
+
existsSync: (path: string) => boolean;
|
|
16
|
+
readFileSync: (path: string, encoding: "utf-8") => string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let fsAdapter: FsAdapter = {
|
|
20
|
+
existsSync: nodeExistsSync,
|
|
21
|
+
readFileSync: nodeReadFileSync,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function setFsAdapter(adapter: FsAdapter): void {
|
|
25
|
+
fsAdapter = adapter;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resetFsAdapter(): void {
|
|
29
|
+
fsAdapter = { existsSync: nodeExistsSync, readFileSync: nodeReadFileSync };
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
const DEFAULT_CONFIG: Config = {
|
|
12
33
|
enabled: true,
|
|
13
34
|
revealedPatterns: [],
|
|
@@ -22,7 +43,7 @@ function findConfigInAncestors(startDir: string): string | null {
|
|
|
22
43
|
|
|
23
44
|
while (true) {
|
|
24
45
|
const configPath = join(currentDir, CONFIG_FILENAME);
|
|
25
|
-
if (existsSync(configPath)) {
|
|
46
|
+
if (fsAdapter.existsSync(configPath)) {
|
|
26
47
|
return configPath;
|
|
27
48
|
}
|
|
28
49
|
|
|
@@ -41,20 +62,18 @@ function findConfigInAncestors(startDir: string): string | null {
|
|
|
41
62
|
}
|
|
42
63
|
|
|
43
64
|
function loadJsonConfig(): Partial<Config> {
|
|
44
|
-
// First, search up from cwd to find nearest config
|
|
45
65
|
const ancestorConfig = findConfigInAncestors(process.cwd());
|
|
46
66
|
if (ancestorConfig) {
|
|
47
67
|
try {
|
|
48
|
-
const content = readFileSync(ancestorConfig, "utf-8");
|
|
68
|
+
const content = fsAdapter.readFileSync(ancestorConfig, "utf-8");
|
|
49
69
|
return JSON.parse(content);
|
|
50
70
|
} catch {}
|
|
51
71
|
}
|
|
52
72
|
|
|
53
|
-
// Fall back to home directory
|
|
54
73
|
const homeConfig = join(homedir(), CONFIG_FILENAME);
|
|
55
|
-
if (existsSync(homeConfig)) {
|
|
74
|
+
if (fsAdapter.existsSync(homeConfig)) {
|
|
56
75
|
try {
|
|
57
|
-
const content = readFileSync(homeConfig, "utf-8");
|
|
76
|
+
const content = fsAdapter.readFileSync(homeConfig, "utf-8");
|
|
58
77
|
return JSON.parse(content);
|
|
59
78
|
} catch {}
|
|
60
79
|
}
|
package/src/masker.ts
CHANGED
|
@@ -21,6 +21,12 @@ const PATTERN_ORDER: PatternConfig[] = [
|
|
|
21
21
|
{ pattern: AWS_PATTERNS.natGateway, type: "nat-gateway" },
|
|
22
22
|
{ pattern: AWS_PATTERNS.networkAcl, type: "network-acl" },
|
|
23
23
|
{ pattern: AWS_PATTERNS.eni, type: "eni" },
|
|
24
|
+
{ pattern: AWS_PATTERNS.vpcEndpoint, type: "vpc-endpoint" },
|
|
25
|
+
{ pattern: AWS_PATTERNS.transitGateway, type: "transit-gateway" },
|
|
26
|
+
{ pattern: AWS_PATTERNS.customerGateway, type: "customer-gateway" },
|
|
27
|
+
{ pattern: AWS_PATTERNS.vpnGateway, type: "vpn-gateway" },
|
|
28
|
+
{ pattern: AWS_PATTERNS.vpnConnection, type: "vpn-connection" },
|
|
29
|
+
{ pattern: AWS_PATTERNS.ecrRepoUri, type: "ecr-repo" },
|
|
24
30
|
{ pattern: AWS_PATTERNS.ami, type: "ami" },
|
|
25
31
|
{ pattern: AWS_PATTERNS.ec2Instance, type: "ec2-instance" },
|
|
26
32
|
{ pattern: AWS_PATTERNS.ebs, type: "ebs" },
|
package/src/patterns.ts
CHANGED
|
@@ -14,7 +14,14 @@ export const AWS_PATTERNS = {
|
|
|
14
14
|
ebs: /^vol-[0-9a-f]{8,17}$/,
|
|
15
15
|
snapshot: /^snap-[0-9a-f]{8,17}$/,
|
|
16
16
|
eni: /^eni-[0-9a-f]{8,17}$/,
|
|
17
|
+
vpcEndpoint: /^vpce-[0-9a-f]{8,17}$/,
|
|
18
|
+
transitGateway: /^tgw-[0-9a-f]{8,17}$/,
|
|
19
|
+
customerGateway: /^cgw-[0-9a-f]{8,17}$/,
|
|
20
|
+
vpnGateway: /^vgw-[0-9a-f]{8,17}$/,
|
|
21
|
+
vpnConnection: /^vpn-[0-9a-f]{8,17}$/,
|
|
17
22
|
accountId: /"(?:OwnerId|AccountId|Owner|account_id)":\s*"(\d{12})"/,
|
|
23
|
+
ecrRepoUri:
|
|
24
|
+
/^(?:\d{12}|#\(custom-\d+\))\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com\/[a-z0-9._\/-]+$/,
|
|
18
25
|
accessKeyId:
|
|
19
26
|
/(?:^|[^A-Z0-9])(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}(?:[^A-Z0-9]|$)/,
|
|
20
27
|
} as const;
|
|
@@ -36,10 +43,3 @@ export const COMMON_PATTERNS = {
|
|
|
36
43
|
apiKeyField:
|
|
37
44
|
/"(?:api_key|apiKey|secret_key|secretKey|access_token|auth_token|password|token)":\s*"([^"]+)"/,
|
|
38
45
|
} as const;
|
|
39
|
-
|
|
40
|
-
// Combined for backward compatibility
|
|
41
|
-
export const PATTERNS = {
|
|
42
|
-
...AWS_PATTERNS,
|
|
43
|
-
...K8S_PATTERNS,
|
|
44
|
-
...COMMON_PATTERNS,
|
|
45
|
-
} as const;
|