@envseal/protocol 0.1.2 → 0.1.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/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const SEP_ERROR_CODES: readonly ["SEP_UNKNOWN_KEY", "SEP_NOT_DECLARED", "SEP_NO_INTERACTIVE_SURFACE", "SEP_TICKET_EXPIRED", "SEP_TICKET_UNKNOWN", "SEP_USER_CANCELLED", "SEP_FORMAT_INVALID", "SEP_SINK_UNAVAILABLE", "SEP_SINK_WRITE_FAILED", "SEP_PROBE_NOT_APPROVED", "SEP_VALUE_IN_REQUEST", "SEP_GITIGNORE_UNSAFE", "SEP_CONFIRMATION_DENIED", "SEP_RATE_LIMITED"];
1
+ export declare const SEP_ERROR_CODES: readonly ["SEP_UNKNOWN_KEY", "SEP_NOT_DECLARED", "SEP_NO_INTERACTIVE_SURFACE", "SEP_TICKET_EXPIRED", "SEP_TICKET_UNKNOWN", "SEP_USER_CANCELLED", "SEP_FORMAT_INVALID", "SEP_SINK_UNAVAILABLE", "SEP_SINK_WRITE_FAILED", "SEP_PROBE_NOT_APPROVED", "SEP_VALUE_IN_REQUEST", "SEP_GITIGNORE_UNSAFE", "SEP_CONFIRMATION_DENIED", "SEP_RATE_LIMITED", "SEP_TARGET_CHANGED", "SEP_PATTERN_UNSAFE", "SEP_KEYS_MISSING"];
2
2
  export type SepErrorCode = (typeof SEP_ERROR_CODES)[number];
3
3
  export interface SepErrorDefaults {
4
4
  retriable: boolean;
package/dist/errors.js CHANGED
@@ -13,6 +13,9 @@ export const SEP_ERROR_CODES = [
13
13
  'SEP_GITIGNORE_UNSAFE',
14
14
  'SEP_CONFIRMATION_DENIED',
15
15
  'SEP_RATE_LIMITED',
16
+ 'SEP_TARGET_CHANGED',
17
+ 'SEP_PATTERN_UNSAFE',
18
+ 'SEP_KEYS_MISSING',
16
19
  ];
17
20
  export const SEP_ERROR_DEFAULTS = {
18
21
  SEP_UNKNOWN_KEY: {
@@ -71,6 +74,19 @@ export const SEP_ERROR_DEFAULTS = {
71
74
  retriable: true,
72
75
  userMessage: 'Rate limited. Please retry later.',
73
76
  },
77
+ SEP_TARGET_CHANGED: {
78
+ retriable: true,
79
+ userMessage: 'The program to run changed on disk after you approved it. Nothing was executed. ' +
80
+ 'Re-run the command to review the current content and approve it again.',
81
+ },
82
+ SEP_PATTERN_UNSAFE: {
83
+ retriable: false,
84
+ userMessage: 'The declared format.pattern is unsafe or too complex. Use a shorter, bounded pattern (for example ^sk-[A-Za-z0-9]{20,80}$).',
85
+ },
86
+ SEP_KEYS_MISSING: {
87
+ retriable: false,
88
+ userMessage: 'One or more requested keys are undeclared or have no stored value. Declare and store them before use.',
89
+ },
74
90
  };
75
91
  export class SepError extends Error {
76
92
  code;
package/dist/schemas.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import { z } from 'zod';
2
+ export declare const FORMAT_PATTERN_MAX_LENGTH = 256;
3
+ export declare const FORMAT_PATTERN_MAX_QUANTIFIER = 256;
4
+ /** Safe subset check for manifest `format.pattern` (length, bounded quantifiers, no nested +/*). */
5
+ export declare function isLinearishRegex(pattern: string): boolean;
2
6
  export declare const ManifestEntry: z.ZodEffects<z.ZodObject<{
3
7
  key: z.ZodString;
4
8
  description: z.ZodString;
5
9
  required: z.ZodDefault<z.ZodBoolean>;
6
10
  secret: z.ZodDefault<z.ZodBoolean>;
7
11
  format: z.ZodOptional<z.ZodObject<{
8
- pattern: z.ZodOptional<z.ZodString>;
12
+ pattern: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
9
13
  minLength: z.ZodOptional<z.ZodNumber>;
10
14
  maxLength: z.ZodOptional<z.ZodNumber>;
11
15
  example: z.ZodOptional<z.ZodString>;
@@ -193,7 +197,7 @@ export declare const Manifest: z.ZodObject<{
193
197
  required: z.ZodDefault<z.ZodBoolean>;
194
198
  secret: z.ZodDefault<z.ZodBoolean>;
195
199
  format: z.ZodOptional<z.ZodObject<{
196
- pattern: z.ZodOptional<z.ZodString>;
200
+ pattern: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
197
201
  minLength: z.ZodOptional<z.ZodNumber>;
198
202
  maxLength: z.ZodOptional<z.ZodNumber>;
199
203
  example: z.ZodOptional<z.ZodString>;
@@ -534,13 +538,13 @@ export declare const VerifyResult: z.ZodObject<{
534
538
  message: z.ZodString;
535
539
  checkedAt: z.ZodString;
536
540
  }, "strict", z.ZodTypeAny, {
537
- key: string;
538
541
  message: string;
542
+ key: string;
539
543
  result: "ok" | "auth_failed" | "forbidden" | "rate_limited" | "network_error" | "no_probe" | "probe_not_approved";
540
544
  checkedAt: string;
541
545
  }, {
542
- key: string;
543
546
  message: string;
547
+ key: string;
544
548
  result: "ok" | "auth_failed" | "forbidden" | "rate_limited" | "network_error" | "no_probe" | "probe_not_approved";
545
549
  checkedAt: string;
546
550
  }>;
@@ -699,7 +703,7 @@ export declare const EnvDeclareInput: z.ZodObject<{
699
703
  required: z.ZodDefault<z.ZodBoolean>;
700
704
  secret: z.ZodDefault<z.ZodBoolean>;
701
705
  format: z.ZodOptional<z.ZodObject<{
702
- pattern: z.ZodOptional<z.ZodString>;
706
+ pattern: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
703
707
  minLength: z.ZodOptional<z.ZodNumber>;
704
708
  maxLength: z.ZodOptional<z.ZodNumber>;
705
709
  example: z.ZodOptional<z.ZodString>;
@@ -1005,7 +1009,7 @@ export declare const INPUT_SCHEMAS: {
1005
1009
  required: z.ZodDefault<z.ZodBoolean>;
1006
1010
  secret: z.ZodDefault<z.ZodBoolean>;
1007
1011
  format: z.ZodOptional<z.ZodObject<{
1008
- pattern: z.ZodOptional<z.ZodString>;
1012
+ pattern: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
1009
1013
  minLength: z.ZodOptional<z.ZodNumber>;
1010
1014
  maxLength: z.ZodOptional<z.ZodNumber>;
1011
1015
  example: z.ZodOptional<z.ZodString>;
package/dist/schemas.js CHANGED
@@ -1,4 +1,201 @@
1
1
  import { z } from 'zod';
2
+ export const FORMAT_PATTERN_MAX_LENGTH = 256;
3
+ export const FORMAT_PATTERN_MAX_QUANTIFIER = 256;
4
+ function isQuantifierBrace(pattern, index) {
5
+ return /^\{\d/.test(pattern.slice(index));
6
+ }
7
+ function parseQuantifier(pattern, start) {
8
+ const ch = pattern[start];
9
+ if (ch === '+' || ch === '*' || ch === '?') {
10
+ return { ok: true, end: start + 1 };
11
+ }
12
+ if (ch !== '{' || !isQuantifierBrace(pattern, start)) {
13
+ return { ok: true, end: start };
14
+ }
15
+ const close = pattern.indexOf('}', start + 1);
16
+ if (close === -1) {
17
+ return { ok: false };
18
+ }
19
+ const body = pattern.slice(start + 1, close);
20
+ const comma = body.indexOf(',');
21
+ let minStr;
22
+ let maxStr;
23
+ if (comma === -1) {
24
+ minStr = body;
25
+ maxStr = undefined;
26
+ }
27
+ else {
28
+ minStr = body.slice(0, comma);
29
+ maxStr = body.slice(comma + 1);
30
+ }
31
+ if (minStr !== '' && !/^\d+$/.test(minStr)) {
32
+ return { ok: false };
33
+ }
34
+ if (maxStr !== undefined && maxStr !== '' && !/^\d+$/.test(maxStr)) {
35
+ return { ok: false };
36
+ }
37
+ const min = minStr === '' ? 0 : Number(minStr);
38
+ const max = maxStr === undefined || maxStr === '' ? undefined : Number(maxStr);
39
+ if (!Number.isInteger(min) || min < 0) {
40
+ return { ok: false };
41
+ }
42
+ if (max !== undefined && (!Number.isInteger(max) || max < min)) {
43
+ return { ok: false };
44
+ }
45
+ if (min > FORMAT_PATTERN_MAX_QUANTIFIER) {
46
+ return { ok: false };
47
+ }
48
+ if (max === undefined && comma !== -1) {
49
+ return { ok: false };
50
+ }
51
+ if (max !== undefined && max > FORMAT_PATTERN_MAX_QUANTIFIER) {
52
+ return { ok: false };
53
+ }
54
+ return { ok: true, end: close + 1 };
55
+ }
56
+ function skipCharacterClass(pattern, start) {
57
+ let i = start + 1;
58
+ if (pattern[i] === '^') {
59
+ i++;
60
+ }
61
+ while (i < pattern.length) {
62
+ if (pattern[i] === '\\') {
63
+ i += 2;
64
+ continue;
65
+ }
66
+ if (pattern[i] === ']') {
67
+ return i + 1;
68
+ }
69
+ i++;
70
+ }
71
+ return null;
72
+ }
73
+ function skipGroupHeader(pattern, start) {
74
+ let i = start + 1;
75
+ if (pattern[i] !== '?') {
76
+ return i;
77
+ }
78
+ i++;
79
+ if (pattern[i] === ':') {
80
+ return i + 1;
81
+ }
82
+ if (pattern[i] === '<') {
83
+ i++;
84
+ if (pattern[i] === '=' || pattern[i] === '!') {
85
+ return i + 1;
86
+ }
87
+ return i;
88
+ }
89
+ if (pattern[i] === '=' || pattern[i] === '!') {
90
+ return i + 1;
91
+ }
92
+ while (i < pattern.length && /[a-z-]/.test(pattern[i])) {
93
+ i++;
94
+ }
95
+ if (pattern[i] === ':') {
96
+ return i + 1;
97
+ }
98
+ if (pattern[i] === ')') {
99
+ return i + 1;
100
+ }
101
+ return i;
102
+ }
103
+ function scanLinearishPattern(pattern) {
104
+ const groupHasInnerQuantifier = [];
105
+ const markInnerQuantifier = () => {
106
+ if (groupHasInnerQuantifier.length > 0) {
107
+ groupHasInnerQuantifier[groupHasInnerQuantifier.length - 1] = true;
108
+ }
109
+ };
110
+ const afterAtom = (start, innerQuantified) => {
111
+ const quantifier = parseQuantifier(pattern, start);
112
+ if (!quantifier.ok) {
113
+ return null;
114
+ }
115
+ if (quantifier.end > start) {
116
+ if (innerQuantified) {
117
+ return null;
118
+ }
119
+ markInnerQuantifier();
120
+ }
121
+ return quantifier.end;
122
+ };
123
+ let i = 0;
124
+ while (i < pattern.length) {
125
+ const ch = pattern[i];
126
+ if (ch === '\\') {
127
+ i += 2;
128
+ if (i > pattern.length) {
129
+ return false;
130
+ }
131
+ i = afterAtom(i, false) ?? -1;
132
+ if (i === -1) {
133
+ return false;
134
+ }
135
+ continue;
136
+ }
137
+ if (ch === '[') {
138
+ const end = skipCharacterClass(pattern, i);
139
+ if (end === null) {
140
+ return false;
141
+ }
142
+ const next = afterAtom(end, false);
143
+ if (next === null) {
144
+ return false;
145
+ }
146
+ i = next;
147
+ continue;
148
+ }
149
+ if (ch === '(') {
150
+ i = skipGroupHeader(pattern, i);
151
+ groupHasInnerQuantifier.push(false);
152
+ continue;
153
+ }
154
+ if (ch === ')') {
155
+ if (groupHasInnerQuantifier.length === 0) {
156
+ return false;
157
+ }
158
+ const innerQuantified = groupHasInnerQuantifier.pop();
159
+ i++;
160
+ const next = afterAtom(i, innerQuantified);
161
+ if (next === null) {
162
+ return false;
163
+ }
164
+ i = next;
165
+ continue;
166
+ }
167
+ if (ch === '^' || ch === '$' || ch === '|') {
168
+ i++;
169
+ continue;
170
+ }
171
+ i++;
172
+ const next = afterAtom(i, false);
173
+ if (next === null) {
174
+ return false;
175
+ }
176
+ i = next;
177
+ }
178
+ return groupHasInnerQuantifier.length === 0;
179
+ }
180
+ /** Safe subset check for manifest `format.pattern` (length, bounded quantifiers, no nested +/*). */
181
+ export function isLinearishRegex(pattern) {
182
+ if (pattern.length > FORMAT_PATTERN_MAX_LENGTH) {
183
+ return false;
184
+ }
185
+ try {
186
+ new RegExp(pattern);
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ return scanLinearishPattern(pattern);
192
+ }
193
+ const formatPatternSchema = z
194
+ .string()
195
+ .max(FORMAT_PATTERN_MAX_LENGTH)
196
+ .refine(isLinearishRegex, {
197
+ message: 'format.pattern must be a safe, bounded regular expression',
198
+ });
2
199
  export const ManifestEntry = z
3
200
  .object({
4
201
  key: z.string().regex(/^[A-Z][A-Z0-9_]{0,63}$/),
@@ -7,7 +204,7 @@ export const ManifestEntry = z
7
204
  secret: z.boolean().default(true),
8
205
  format: z
9
206
  .object({
10
- pattern: z.string().optional(),
207
+ pattern: formatPatternSchema.optional(),
11
208
  minLength: z.number().int().optional(),
12
209
  maxLength: z.number().int().optional(),
13
210
  example: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envseal/protocol",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",