@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.
@@ -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,9 +114,16 @@ 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 {
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 (existsSync2(configPath)) {
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 = readFileSync2(ancestorConfig, "utf-8");
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 (existsSync2(homeConfig)) {
161
+ if (fsAdapter.existsSync(homeConfig)) {
154
162
  try {
155
- const content = readFileSync2(homeConfig, "utf-8");
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniglot/wont-let-you-see",
3
- "version": "0.1.1",
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,15 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
- import { getConfig, resetConfig, isPatternEnabled } from "../config";
3
- import * as fs from "fs";
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.mock("fs", async () => {
7
- const actual = await vi.importActual<typeof fs>("fs");
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
- vi.mocked(fs.existsSync).mockReturnValue(false);
22
- vi.mocked(fs.readFileSync).mockReturnValue("{}");
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
- vi.mocked(fs.existsSync).mockImplementation((p) => {
123
+ mockExistsSync.mockImplementation((p: string) => {
120
124
  return p === path.join("/project", ".wont-let-you-see.json");
121
125
  });
122
- vi.mocked(fs.readFileSync).mockReturnValue(
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
- vi.mocked(fs.existsSync).mockImplementation((p) => {
138
+ mockExistsSync.mockImplementation((p: string) => {
135
139
  return p === path.join("/project", ".wont-let-you-see.json");
136
140
  });
137
- vi.mocked(fs.readFileSync).mockReturnValue(
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
- vi.mocked(fs.existsSync).mockImplementation((p) => {
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
- vi.mocked(fs.readFileSync).mockImplementation((p) => {
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
- vi.mocked(fs.existsSync).mockImplementation((p) => {
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
- vi.mocked(fs.readFileSync).mockImplementation((p) => {
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
- 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, dirname, parse } 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: [],
@@ -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;