@uniglot/wont-let-you-see 0.1.0 → 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.
@@ -0,0 +1 @@
1
+ * @uniglot
@@ -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
@@ -0,0 +1,13 @@
1
+ name: Test
2
+ on:
3
+ pull_request:
4
+ branches: [main]
5
+
6
+ jobs:
7
+ test:
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 test
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,27 +114,55 @@ function getOriginal(sessionId, token) {
113
114
  }
114
115
 
115
116
  // src/config.ts
116
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
117
- import { join as join2 } from "path";
117
+ import {
118
+ existsSync as nodeExistsSync,
119
+ readFileSync as nodeReadFileSync
120
+ } from "fs";
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: [],
122
130
  customPatterns: []
123
131
  };
124
132
  var CONFIG_FILENAME = ".wont-let-you-see.json";
125
- function loadJsonConfig() {
126
- const paths = [
127
- join2(process.cwd(), CONFIG_FILENAME),
128
- join2(homedir2(), CONFIG_FILENAME)
129
- ];
130
- for (const configPath of paths) {
131
- if (existsSync2(configPath)) {
132
- try {
133
- const content = readFileSync2(configPath, "utf-8");
134
- return JSON.parse(content);
135
- } catch {}
133
+ function findConfigInAncestors(startDir) {
134
+ const home = homedir2();
135
+ let currentDir = startDir;
136
+ while (true) {
137
+ const configPath = join2(currentDir, CONFIG_FILENAME);
138
+ if (fsAdapter.existsSync(configPath)) {
139
+ return configPath;
140
+ }
141
+ if (currentDir === home) {
142
+ break;
136
143
  }
144
+ const parentDir = dirname(currentDir);
145
+ if (parentDir === currentDir) {
146
+ break;
147
+ }
148
+ currentDir = parentDir;
149
+ }
150
+ return null;
151
+ }
152
+ function loadJsonConfig() {
153
+ const ancestorConfig = findConfigInAncestors(process.cwd());
154
+ if (ancestorConfig) {
155
+ try {
156
+ const content = fsAdapter.readFileSync(ancestorConfig, "utf-8");
157
+ return JSON.parse(content);
158
+ } catch {}
159
+ }
160
+ const homeConfig = join2(homedir2(), CONFIG_FILENAME);
161
+ if (fsAdapter.existsSync(homeConfig)) {
162
+ try {
163
+ const content = fsAdapter.readFileSync(homeConfig, "utf-8");
164
+ return JSON.parse(content);
165
+ } catch {}
137
166
  }
138
167
  return {};
139
168
  }
@@ -189,6 +218,12 @@ var PATTERN_ORDER = [
189
218
  { pattern: AWS_PATTERNS.natGateway, type: "nat-gateway" },
190
219
  { pattern: AWS_PATTERNS.networkAcl, type: "network-acl" },
191
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" },
192
227
  { pattern: AWS_PATTERNS.ami, type: "ami" },
193
228
  { pattern: AWS_PATTERNS.ec2Instance, type: "ec2-instance" },
194
229
  { pattern: AWS_PATTERNS.ebs, type: "ebs" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniglot/wont-let-you-see",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "OpenCode plugin that masks sensitive cloud infrastructure data (AWS, Kubernetes) from LLMs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,16 +1,36 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { getConfig, resetConfig, isPatternEnabled } from "../config";
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import {
3
+ getConfig,
4
+ resetConfig,
5
+ isPatternEnabled,
6
+ setFsAdapter,
7
+ resetFsAdapter,
8
+ } from "../config";
9
+ import * as path from "path";
10
+
11
+ const mockExistsSync = vi.fn();
12
+ const mockReadFileSync = vi.fn();
3
13
 
4
14
  describe("Config", () => {
5
15
  const originalEnv = { ...process.env };
16
+ const originalCwd = process.cwd;
6
17
 
7
18
  beforeEach(() => {
8
19
  resetConfig();
20
+ setFsAdapter({
21
+ existsSync: mockExistsSync,
22
+ readFileSync: mockReadFileSync,
23
+ });
24
+ mockExistsSync.mockReturnValue(false);
25
+ mockReadFileSync.mockReturnValue("{}");
9
26
  });
10
27
 
11
28
  afterEach(() => {
12
29
  process.env = { ...originalEnv };
30
+ process.cwd = originalCwd;
13
31
  resetConfig();
32
+ resetFsAdapter();
33
+ vi.clearAllMocks();
14
34
  });
15
35
 
16
36
  describe("getConfig", () => {
@@ -95,4 +115,77 @@ describe("Config", () => {
95
115
  expect(isPatternEnabled("vpc")).toBe(true);
96
116
  });
97
117
  });
118
+
119
+ describe("ancestor directory config lookup", () => {
120
+ it("should find config in parent directory when cwd is subdirectory", () => {
121
+ process.cwd = () => "/project/packages/app";
122
+
123
+ mockExistsSync.mockImplementation((p: string) => {
124
+ return p === path.join("/project", ".wont-let-you-see.json");
125
+ });
126
+ mockReadFileSync.mockReturnValue(
127
+ JSON.stringify({ enabled: false, customPatterns: ["secret123"] }),
128
+ );
129
+
130
+ const config = getConfig();
131
+ expect(config.enabled).toBe(false);
132
+ expect(config.customPatterns).toEqual(["secret123"]);
133
+ });
134
+
135
+ it("should find config in grandparent directory", () => {
136
+ process.cwd = () => "/project/packages/app/src/components";
137
+
138
+ mockExistsSync.mockImplementation((p: string) => {
139
+ return p === path.join("/project", ".wont-let-you-see.json");
140
+ });
141
+ mockReadFileSync.mockReturnValue(
142
+ JSON.stringify({ revealedPatterns: ["ipv4"] }),
143
+ );
144
+
145
+ const config = getConfig();
146
+ expect(config.revealedPatterns).toEqual(["ipv4"]);
147
+ });
148
+
149
+ it("should prefer config in closer ancestor over distant ancestor", () => {
150
+ process.cwd = () => "/project/packages/app";
151
+
152
+ mockExistsSync.mockImplementation((p: string) => {
153
+ return (
154
+ p === path.join("/project/packages/app", ".wont-let-you-see.json") ||
155
+ p === path.join("/project", ".wont-let-you-see.json")
156
+ );
157
+ });
158
+ mockReadFileSync.mockImplementation((p: string) => {
159
+ if (
160
+ p === path.join("/project/packages/app", ".wont-let-you-see.json")
161
+ ) {
162
+ return JSON.stringify({ customPatterns: ["app-secret"] });
163
+ }
164
+ return JSON.stringify({ customPatterns: ["root-secret"] });
165
+ });
166
+
167
+ const config = getConfig();
168
+ expect(config.customPatterns).toEqual(["app-secret"]);
169
+ });
170
+
171
+ it("should prefer cwd config over parent config", () => {
172
+ process.cwd = () => "/project/subdir";
173
+
174
+ mockExistsSync.mockImplementation((p: string) => {
175
+ return (
176
+ p === path.join("/project/subdir", ".wont-let-you-see.json") ||
177
+ p === path.join("/project", ".wont-let-you-see.json")
178
+ );
179
+ });
180
+ mockReadFileSync.mockImplementation((p: string) => {
181
+ if (p === path.join("/project/subdir", ".wont-let-you-see.json")) {
182
+ return JSON.stringify({ enabled: false });
183
+ }
184
+ return JSON.stringify({ enabled: true });
185
+ });
186
+
187
+ const config = getConfig();
188
+ expect(config.enabled).toBe(false);
189
+ });
190
+ });
98
191
  });
@@ -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
- for (const pattern of Object.values(PATTERNS)) {
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 { existsSync, readFileSync } from "fs";
2
- import { join } from "path";
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: [],
@@ -16,19 +37,45 @@ const DEFAULT_CONFIG: Config = {
16
37
 
17
38
  const CONFIG_FILENAME = ".wont-let-you-see.json";
18
39
 
19
- function loadJsonConfig(): Partial<Config> {
20
- const paths = [
21
- join(process.cwd(), CONFIG_FILENAME),
22
- join(homedir(), CONFIG_FILENAME),
23
- ];
24
-
25
- for (const configPath of paths) {
26
- if (existsSync(configPath)) {
27
- try {
28
- const content = readFileSync(configPath, "utf-8");
29
- return JSON.parse(content);
30
- } catch {}
40
+ function findConfigInAncestors(startDir: string): string | null {
41
+ const home = homedir();
42
+ let currentDir = startDir;
43
+
44
+ while (true) {
45
+ const configPath = join(currentDir, CONFIG_FILENAME);
46
+ if (fsAdapter.existsSync(configPath)) {
47
+ return configPath;
31
48
  }
49
+
50
+ if (currentDir === home) {
51
+ break;
52
+ }
53
+
54
+ const parentDir = dirname(currentDir);
55
+ if (parentDir === currentDir) {
56
+ break;
57
+ }
58
+ currentDir = parentDir;
59
+ }
60
+
61
+ return null;
62
+ }
63
+
64
+ function loadJsonConfig(): Partial<Config> {
65
+ const ancestorConfig = findConfigInAncestors(process.cwd());
66
+ if (ancestorConfig) {
67
+ try {
68
+ const content = fsAdapter.readFileSync(ancestorConfig, "utf-8");
69
+ return JSON.parse(content);
70
+ } catch {}
71
+ }
72
+
73
+ const homeConfig = join(homedir(), CONFIG_FILENAME);
74
+ if (fsAdapter.existsSync(homeConfig)) {
75
+ try {
76
+ const content = fsAdapter.readFileSync(homeConfig, "utf-8");
77
+ return JSON.parse(content);
78
+ } catch {}
32
79
  }
33
80
 
34
81
  return {};
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;