@ninedeploy/schemas 0.2.7 → 0.3.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.
Files changed (45) hide show
  1. package/dist/auth.d.ts +19 -14
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +18 -4
  4. package/dist/auth.js.map +1 -1
  5. package/dist/common.d.ts +18 -0
  6. package/dist/common.d.ts.map +1 -1
  7. package/dist/common.js +47 -0
  8. package/dist/common.js.map +1 -1
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/insights.d.ts +110 -0
  14. package/dist/insights.d.ts.map +1 -0
  15. package/dist/insights.js +64 -0
  16. package/dist/insights.js.map +1 -0
  17. package/dist/labels.d.ts +108 -0
  18. package/dist/labels.d.ts.map +1 -0
  19. package/dist/labels.js +70 -0
  20. package/dist/labels.js.map +1 -0
  21. package/dist/management.d.ts +6 -7
  22. package/dist/management.d.ts.map +1 -1
  23. package/dist/management.js +34 -9
  24. package/dist/management.js.map +1 -1
  25. package/dist/ninedeployManifest.d.ts +292 -0
  26. package/dist/ninedeployManifest.d.ts.map +1 -0
  27. package/dist/ninedeployManifest.js +284 -0
  28. package/dist/ninedeployManifest.js.map +1 -0
  29. package/dist/project.d.ts +4 -2
  30. package/dist/project.d.ts.map +1 -1
  31. package/dist/project.js +2 -2
  32. package/dist/project.js.map +1 -1
  33. package/dist/secretScan.d.ts +52 -0
  34. package/dist/secretScan.d.ts.map +1 -0
  35. package/dist/secretScan.js +125 -0
  36. package/dist/secretScan.js.map +1 -0
  37. package/dist/service.d.ts +118 -4
  38. package/dist/service.d.ts.map +1 -1
  39. package/dist/service.js +148 -10
  40. package/dist/service.js.map +1 -1
  41. package/dist/workspaces.d.ts +49 -0
  42. package/dist/workspaces.d.ts.map +1 -1
  43. package/dist/workspaces.js +9 -0
  44. package/dist/workspaces.js.map +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,284 @@
1
+ import { z } from 'zod';
2
+ import { envVarName, httpPath, repoBaseDir, slug } from './common.js';
3
+ /**
4
+ * `.ninedeploy` manifest schema — project-side declarative config for how
5
+ * NineDeploy should build, run and route a service. Committed to the repo,
6
+ * so it must NEVER carry secrets, tokens or connection strings; the loader
7
+ * enforces that with a regex scan (see apps/server/src/lib/secretScan.ts).
8
+ *
9
+ * Merge precedence at apply time: panel/DB > manifest > auto-detect. The
10
+ * manifest fills empty panel fields; an operator who explicitly set something
11
+ * in the panel wins.
12
+ */
13
+ // ── Runtime ────────────────────────────────────────────────────────────────
14
+ export const runtimeType = z.enum([
15
+ 'auto',
16
+ 'node',
17
+ 'python',
18
+ 'go',
19
+ 'ruby',
20
+ 'php',
21
+ 'java',
22
+ 'rust',
23
+ 'static',
24
+ ]);
25
+ export const runtime = z
26
+ .object({
27
+ type: runtimeType.default('auto'),
28
+ /**
29
+ * Pinned language version. Format `<major>` or `<major>.<minor>` or
30
+ * `<major>.<minor>.<patch>`. For example `20`, `20.18`, `3.12`.
31
+ */
32
+ version: z
33
+ .string()
34
+ .regex(/^\d+(?:\.\d+){0,2}$/, 'must be a numeric version like 20 or 3.12')
35
+ .max(20)
36
+ .optional(),
37
+ })
38
+ .strict();
39
+ // ── Build ──────────────────────────────────────────────────────────────────
40
+ export const build = z
41
+ .object({
42
+ install: z.string().min(1).max(500).optional(),
43
+ build: z.string().min(1).max(500).optional(),
44
+ start: z.string().min(1).max(500).optional(),
45
+ baseDir: repoBaseDir.optional(),
46
+ dockerfile: z.string().min(1).max(200).optional(),
47
+ })
48
+ .strict();
49
+ // ── Run ────────────────────────────────────────────────────────────────────
50
+ export const restartPolicy = z
51
+ .union([
52
+ z.enum(['no', 'always', 'unless-stopped']),
53
+ z
54
+ .string()
55
+ .regex(/^on-failure(?::[1-9]\d{0,2})?$/, 'must be on-failure or on-failure:N (1-999, no leading zero)'),
56
+ ])
57
+ .optional();
58
+ export const run = z
59
+ .object({
60
+ port: z.number().int().min(1).max(65535).optional(),
61
+ healthcheck: httpPath.optional(),
62
+ restart: restartPolicy,
63
+ })
64
+ .strict();
65
+ // ── Static ─────────────────────────────────────────────────────────────────
66
+ const noTraversalSegments = (value) => !value.split('/').some((segment) => segment === '..' || segment === '.');
67
+ const isRelativePath = (value) => !value.startsWith('/');
68
+ export const staticConfig = z
69
+ .object({
70
+ spa: z.boolean().default(false),
71
+ root: z
72
+ .string()
73
+ .min(1)
74
+ .max(200)
75
+ .regex(/^[A-Za-z0-9._/-]+$/, 'must be a relative path with allowed characters')
76
+ .refine(noTraversalSegments, { message: 'path must not contain . or .. segments' })
77
+ .refine(isRelativePath, { message: 'path must be relative (no leading "/")' })
78
+ .optional(),
79
+ })
80
+ .strict();
81
+ // ── Env ────────────────────────────────────────────────────────────────────
82
+ export const env = z
83
+ .object({
84
+ /** Required env var names. Values stay in the panel's env vault. */
85
+ required: z.array(envVarName.max(100)).max(100).default([]),
86
+ /** Managed-DB attach alias: env-key → attachment-env-key. */
87
+ aliases: z.record(z.string(), envVarName).optional(),
88
+ })
89
+ .strict();
90
+ // ── Phases (Nixpacks ince ayar) ────────────────────────────────────────────
91
+ export const phases = z
92
+ .object({
93
+ setup: z
94
+ .object({
95
+ pkgs: z.array(z.string().min(1).max(100)).max(50).default([]),
96
+ })
97
+ .strict()
98
+ .optional(),
99
+ build: z
100
+ .object({
101
+ cmds: z.array(z.string().min(1).max(500)).max(20).default([]),
102
+ })
103
+ .strict()
104
+ .optional(),
105
+ })
106
+ .strict();
107
+ // ── Resources ──────────────────────────────────────────────────────────────
108
+ export const resources = z
109
+ .object({
110
+ cpuShares: z.number().int().min(0).max(262144).optional(),
111
+ memMb: z.number().int().min(0).max(1_048_576).optional(),
112
+ })
113
+ .strict();
114
+ // ── Hooks ──────────────────────────────────────────────────────────────────
115
+ export const hooks = z
116
+ .object({
117
+ preBuild: z.string().min(1).max(500).optional(),
118
+ postBuild: z.string().min(1).max(500).optional(),
119
+ preStop: z.string().min(1).max(500).optional(),
120
+ })
121
+ .strict();
122
+ // ── Watch (monorepo) ──────────────────────────────────────────────────────
123
+ export const watch = z
124
+ .object({
125
+ paths: z.array(z.string().min(1).max(200)).max(50).default([]),
126
+ })
127
+ .strict();
128
+ // ── Routes ─────────────────────────────────────────────────────────────────
129
+ export const rateLimit = z
130
+ .object({
131
+ average: z.number().int().min(0).max(100_000),
132
+ burst: z.number().int().min(0).max(100_000),
133
+ })
134
+ .strict();
135
+ export const route = z
136
+ .object({
137
+ host: z
138
+ .string()
139
+ .min(3)
140
+ .max(253)
141
+ .regex(/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?:\.(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*$/, 'must be a valid hostname'),
142
+ path: z
143
+ .string()
144
+ .min(1)
145
+ .max(200)
146
+ .regex(/^\/.*$/, 'must start with "/"')
147
+ .default('/'),
148
+ ssl: z.boolean().default(true),
149
+ redirectWww: z.boolean().optional(),
150
+ headers: z.record(z.string().min(1).max(200), z.string().min(1).max(2000)).optional(),
151
+ ipAllowlist: z
152
+ .array(z
153
+ .string()
154
+ .min(1)
155
+ .max(64)
156
+ .regex(/^[\d./]+$/, 'must be a CIDR like 1.2.3.4/32'))
157
+ .max(50)
158
+ .optional(),
159
+ rateLimit: rateLimit.optional(),
160
+ })
161
+ .strict();
162
+ // ── Previews ──────────────────────────────────────────────────────────────
163
+ export const previews = z
164
+ .object({
165
+ enabled: z.boolean().default(false),
166
+ /**
167
+ * Hostname template. `{n}` is replaced by the PR number. Example:
168
+ * `pr-{n}.previews.example.com`. Must contain `{n}` when enabled.
169
+ */
170
+ pattern: z
171
+ .string()
172
+ .min(1)
173
+ .max(200)
174
+ .regex(/^[A-Za-z0-9._/{}*-]+$/, 'must be a hostname template (letters, digits, dot, dash, underscore, slash, brace, asterisk)')
175
+ .refine(noTraversalSegments, { message: 'pattern must not contain . or .. segments' })
176
+ .optional(),
177
+ maxActive: z.number().int().min(1).max(50).default(5),
178
+ autoDestroyOnClose: z.boolean().default(true),
179
+ })
180
+ .strict()
181
+ .refine((p) => !p.enabled || (p.pattern != null && p.pattern.includes('{n}')), {
182
+ message: 'previews.pattern must contain {n} when previews.enabled is true',
183
+ path: ['pattern'],
184
+ });
185
+ // ── Volume ────────────────────────────────────────────────────────────────
186
+ export const volumeBackups = z
187
+ .object({
188
+ schedule: z
189
+ .string()
190
+ .min(1)
191
+ .max(100)
192
+ .regex(/^[\d*\/,\-A-Za-z\s]+$/, 'must look like a cron expression (digits, *, /, comma, dash, letters, space)'),
193
+ retention: z.number().int().min(1).max(365).default(7),
194
+ })
195
+ .strict();
196
+ export const volume = z
197
+ .object({
198
+ mount: z
199
+ .string()
200
+ .min(1)
201
+ .max(200)
202
+ .regex(/^[A-Za-z0-9._/-]+$/, 'must be a path inside the container')
203
+ .refine(noTraversalSegments, { message: 'mount must not contain . or .. segments' })
204
+ .optional(),
205
+ backups: volumeBackups.optional(),
206
+ })
207
+ .strict();
208
+ // ── Database (managed DB attach hint) ─────────────────────────────────────
209
+ export const database = z
210
+ .object({
211
+ ref: slug,
212
+ env: envVarName,
213
+ })
214
+ .strict();
215
+ // ── Network ───────────────────────────────────────────────────────────────
216
+ export const network = z
217
+ .object({
218
+ publishPort: z.number().int().min(1).max(65535).optional(),
219
+ aliases: z.array(z.string().min(1).max(100).regex(/^[A-Za-z0-9._-]+$/)).max(20).default([]),
220
+ })
221
+ .strict();
222
+ // ── Notifications (channel-name references) ──────────────────────────────
223
+ export const notifications = z
224
+ .object({
225
+ onDeploy: z.array(z.string().min(1).max(100)).max(20).default([]),
226
+ onFailure: z.array(z.string().min(1).max(100)).max(20).default([]),
227
+ onAlert: z.array(z.string().min(1).max(100)).max(20).default([]),
228
+ })
229
+ .strict();
230
+ // ── Alerts ────────────────────────────────────────────────────────────────
231
+ export const alertWhen = z.enum([
232
+ 'deployFailed',
233
+ 'restartLoop',
234
+ 'highMemory',
235
+ 'highCpu',
236
+ 'certExpiry',
237
+ ]);
238
+ export const alert = z
239
+ .object({
240
+ when: alertWhen,
241
+ channel: z.string().min(1).max(100),
242
+ thresholdPct: z.number().int().min(1).max(100).optional(),
243
+ })
244
+ .strict()
245
+ .refine((a) => a.when !== 'highMemory' && a.when !== 'highCpu' ? true : a.thresholdPct != null, {
246
+ message: 'thresholdPct is required for highMemory/highCpu alerts',
247
+ path: ['thresholdPct'],
248
+ });
249
+ // ── Top-level manifest ────────────────────────────────────────────────────
250
+ export const ninedeployManifest = z
251
+ .object({
252
+ version: z.literal('1'),
253
+ runtime: runtime.optional(),
254
+ build: build.optional(),
255
+ run: run.optional(),
256
+ static: staticConfig.optional(),
257
+ env: env.optional(),
258
+ phases: phases.optional(),
259
+ resources: resources.optional(),
260
+ hooks: hooks.optional(),
261
+ watch: watch.optional(),
262
+ routes: z.array(route).max(50).optional(),
263
+ previews: previews.optional(),
264
+ volume: volume.optional(),
265
+ database: database.optional(),
266
+ network: network.optional(),
267
+ notifications: notifications.optional(),
268
+ alerts: z.array(alert).max(20).optional(),
269
+ })
270
+ .strict();
271
+ /**
272
+ * The list of filenames the loader checks, in priority order. The first one
273
+ * found in the repo root is used.
274
+ */
275
+ export const NINEDEPLOY_MANIFEST_FILENAMES = [
276
+ '.ninedeploy',
277
+ '.ninedeploy.yml',
278
+ '.ninedeploy.yaml',
279
+ 'ninedeploy.yml',
280
+ 'ninedeploy.yaml',
281
+ ];
282
+ /** Maximum allowed size of a `.ninedeploy` file (bytes). Larger files are refused. */
283
+ export const NINEDEPLOY_MANIFEST_MAX_BYTES = 16 * 1024;
284
+ //# sourceMappingURL=ninedeployManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ninedeployManifest.js","sourceRoot":"","sources":["../src/ninedeployManifest.ts"],"names":[],"mappings":"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"}
package/dist/project.d.ts CHANGED
@@ -7,8 +7,8 @@ import { z } from 'zod';
7
7
  export declare const createProject: z.ZodObject<{
8
8
  name: z.ZodString;
9
9
  slug: z.ZodOptional<z.ZodString>;
10
- description: z.ZodOptional<z.ZodString>;
11
- workspaceId: z.ZodOptional<z.ZodNumber>;
10
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
11
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
12
12
  }, z.core.$strip>;
13
13
  export type CreateProject = z.infer<typeof createProject>;
14
14
  export type CreateProjectInput = CreateProject;
@@ -23,6 +23,8 @@ export type ProjectPatchInput = ProjectPatch;
23
23
  export interface ProjectEntry {
24
24
  id: number;
25
25
  workspaceId?: number | null;
26
+ /** Display name of the owning workspace. Resolved server-side. */
27
+ workspaceName?: string | null;
26
28
  name: string;
27
29
  slug: string;
28
30
  description: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;GAIG;AAEH,eAAO,MAAM,aAAa;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAE/C,eAAO,MAAM,YAAY;;;;iBAMoD,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AACxD,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE7C,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;GAIG;AAEH,eAAO,MAAM,aAAa;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAE/C,eAAO,MAAM,YAAY;;;;iBAMoD,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AACxD,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE7C,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
package/dist/project.js CHANGED
@@ -8,8 +8,8 @@ import { slug } from './common.js';
8
8
  export const createProject = z.object({
9
9
  name: z.string().trim().min(2).max(63),
10
10
  slug: slug.optional(),
11
- description: z.string().trim().max(500).optional(),
12
- workspaceId: z.number().int().positive().optional(),
11
+ description: z.string().trim().max(500).nullable().optional(),
12
+ workspaceId: z.number().int().positive().nullable().optional(),
13
13
  });
14
14
  export const projectPatch = z
15
15
  .object({
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC;;;;GAIG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACtC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACpD,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC/D,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC;;;;GAIG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACtC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC/D,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC/D,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Secret-pattern scanner for `.ninedeploy` manifests.
3
+ *
4
+ * The manifest is committed to the repo, so it must NEVER carry real
5
+ * credentials, tokens or connection strings. This module is a defensive net:
6
+ * patterns are matched against the raw text, and any hit fails the load.
7
+ *
8
+ * The list is intentionally explicit (named patterns) rather than a generic
9
+ * entropy heuristic — false positives are user-hostile (legitimate values
10
+ * that look credential-like should pass). Every regex is also bounded to
11
+ * avoid runaway matching on long inputs.
12
+ *
13
+ * Lives in `@ninedeploy/schemas` (not in `@ninedeploy/server`) because the
14
+ * CLI also needs to run the scan in `ninedeploy manifest validate`. Anything
15
+ * that loads a manifest — server, CLI, future MCP tool — should reuse this.
16
+ */
17
+ /** A single rule: a human-readable name plus a regex that MUST match the
18
+ * *beginning* of a credential — anchoring left keeps the regex from firing
19
+ * on substrings inside longer strings. */
20
+ export interface SecretPattern {
21
+ readonly id: string;
22
+ readonly description: string;
23
+ readonly regex: RegExp;
24
+ }
25
+ export declare const SECRET_PATTERNS: ReadonlyArray<SecretPattern>;
26
+ /** A single secret-pattern hit in scanned text. */
27
+ export interface SecretHit {
28
+ patternId: string;
29
+ description: string;
30
+ /** Index in the original text where the match starts. */
31
+ start: number;
32
+ /** Index in the original text where the match ends. */
33
+ end: number;
34
+ /** A redacted preview of the matched text (first 4 chars + … + last 2). */
35
+ redacted: string;
36
+ }
37
+ /**
38
+ * Run every secret pattern across `text` and return every hit.
39
+ *
40
+ * The scan is non-overlapping and bounded — each regex is `\b`-anchored or
41
+ * scheme-anchored, so a single credential can never span more than one match.
42
+ */
43
+ export declare function scanForSecrets(text: string): SecretHit[];
44
+ /** Returns true if the text contains anything that looks like a secret. */
45
+ export declare function hasSecret(text: string): boolean;
46
+ /**
47
+ * Replace the middle of a string with `…` for safe logging. Keeps the first
48
+ * 4 and last 2 characters when the input is long enough; otherwise just
49
+ * emits `<redacted:N>` so the length is still visible.
50
+ */
51
+ export declare function redact(value: string): string;
52
+ //# sourceMappingURL=secretScan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretScan.d.ts","sourceRoot":"","sources":["../src/secretScan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;2CAE2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,aAAa,CAkExD,CAAC;AAEF,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAmBxD;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5C"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Secret-pattern scanner for `.ninedeploy` manifests.
3
+ *
4
+ * The manifest is committed to the repo, so it must NEVER carry real
5
+ * credentials, tokens or connection strings. This module is a defensive net:
6
+ * patterns are matched against the raw text, and any hit fails the load.
7
+ *
8
+ * The list is intentionally explicit (named patterns) rather than a generic
9
+ * entropy heuristic — false positives are user-hostile (legitimate values
10
+ * that look credential-like should pass). Every regex is also bounded to
11
+ * avoid runaway matching on long inputs.
12
+ *
13
+ * Lives in `@ninedeploy/schemas` (not in `@ninedeploy/server`) because the
14
+ * CLI also needs to run the scan in `ninedeploy manifest validate`. Anything
15
+ * that loads a manifest — server, CLI, future MCP tool — should reuse this.
16
+ */
17
+ export const SECRET_PATTERNS = [
18
+ {
19
+ id: 'aws-access-key',
20
+ description: 'AWS access key id (AKIA…)',
21
+ regex: /AKIA[0-9A-Z]{16}\b/,
22
+ },
23
+ {
24
+ id: 'github-pat-classic',
25
+ description: 'GitHub personal access token (ghp_…)',
26
+ regex: /\bghp_[A-Za-z0-9]{36}\b/,
27
+ },
28
+ {
29
+ id: 'github-pat-fine-grained',
30
+ description: 'GitHub fine-grained PAT (github_pat_…)',
31
+ regex: /\bgithub_pat_[A-Za-z0-9_]{82}\b/,
32
+ },
33
+ {
34
+ id: 'gitlab-pat',
35
+ description: 'GitLab personal access token (glpat-…)',
36
+ regex: /\bglpat-[A-Za-z0-9_-]{20,}\b/,
37
+ },
38
+ {
39
+ id: 'slack-token',
40
+ description: 'Slack token (xox[baprs]-…)',
41
+ regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
42
+ },
43
+ {
44
+ id: 'stripe-live-secret',
45
+ description: 'Stripe live secret key (sk_live_…)',
46
+ regex: /\bsk_live_[A-Za-z0-9]{24,}\b/,
47
+ },
48
+ {
49
+ id: 'stripe-live-restricted',
50
+ description: 'Stripe live restricted key (rk_live_…)',
51
+ regex: /\brk_live_[A-Za-z0-9]{24,}\b/,
52
+ },
53
+ {
54
+ id: 'openai-secret',
55
+ description: 'OpenAI API key (sk-…)',
56
+ regex: /\bsk-[A-Za-z0-9]{48,}\b/,
57
+ },
58
+ {
59
+ id: 'anthropic-key',
60
+ description: 'Anthropic API key (sk-ant-…)',
61
+ regex: /\bsk-ant-[A-Za-z0-9_-]{40,}\b/,
62
+ },
63
+ {
64
+ id: 'discord-webhook',
65
+ description: 'Discord webhook URL (discord…/api/webhooks/…)',
66
+ regex: /https?:\/\/(?:[A-Za-z0-9-]+\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_-]+/,
67
+ },
68
+ {
69
+ id: 'database-url-credentials',
70
+ description: 'Database URL with embedded credentials (scheme://user:pass@…)',
71
+ regex: /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqps?|mssql):\/\/[^\s:@'"]+:[^\s@'"]{3,}@/i,
72
+ },
73
+ {
74
+ id: 'private-key',
75
+ description: 'PEM private key block',
76
+ regex: /-----BEGIN [A-Z ]+PRIVATE KEY-----/,
77
+ },
78
+ {
79
+ id: 'jwt-bearer',
80
+ description: 'Bearer JWT in a literal value',
81
+ regex: /\bBearer\s+eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/,
82
+ },
83
+ ];
84
+ /**
85
+ * Run every secret pattern across `text` and return every hit.
86
+ *
87
+ * The scan is non-overlapping and bounded — each regex is `\b`-anchored or
88
+ * scheme-anchored, so a single credential can never span more than one match.
89
+ */
90
+ export function scanForSecrets(text) {
91
+ const hits = [];
92
+ for (const pattern of SECRET_PATTERNS) {
93
+ // Reset stateful `g` regexes (defensive — none of ours use `g` today).
94
+ pattern.regex.lastIndex = 0;
95
+ const match = pattern.regex.exec(text);
96
+ if (!match || match.index === undefined)
97
+ continue;
98
+ const start = match.index;
99
+ const end = start + match[0].length;
100
+ hits.push({
101
+ patternId: pattern.id,
102
+ description: pattern.description,
103
+ start,
104
+ end,
105
+ redacted: redact(match[0]),
106
+ });
107
+ }
108
+ // Sort by start position so the redacted-log line reads in document order.
109
+ return hits.sort((a, b) => a.start - b.start);
110
+ }
111
+ /** Returns true if the text contains anything that looks like a secret. */
112
+ export function hasSecret(text) {
113
+ return scanForSecrets(text).length > 0;
114
+ }
115
+ /**
116
+ * Replace the middle of a string with `…` for safe logging. Keeps the first
117
+ * 4 and last 2 characters when the input is long enough; otherwise just
118
+ * emits `<redacted:N>` so the length is still visible.
119
+ */
120
+ export function redact(value) {
121
+ if (value.length <= 8)
122
+ return `<redacted:${value.length}>`;
123
+ return `${value.slice(0, 4)}…${value.slice(-2)} (len=${value.length})`;
124
+ }
125
+ //# sourceMappingURL=secretScan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretScan.js","sourceRoot":"","sources":["../src/secretScan.ts"],"names":[],"mappings":"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"}