@envseal/protocol 0.1.3 → 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 +1 -1
- package/dist/errors.js +10 -0
- package/dist/schemas.d.ts +10 -6
- package/dist/schemas.js +198 -1
- package/package.json +1 -1
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", "SEP_TARGET_CHANGED"];
|
|
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
|
@@ -14,6 +14,8 @@ export const SEP_ERROR_CODES = [
|
|
|
14
14
|
'SEP_CONFIRMATION_DENIED',
|
|
15
15
|
'SEP_RATE_LIMITED',
|
|
16
16
|
'SEP_TARGET_CHANGED',
|
|
17
|
+
'SEP_PATTERN_UNSAFE',
|
|
18
|
+
'SEP_KEYS_MISSING',
|
|
17
19
|
];
|
|
18
20
|
export const SEP_ERROR_DEFAULTS = {
|
|
19
21
|
SEP_UNKNOWN_KEY: {
|
|
@@ -77,6 +79,14 @@ export const SEP_ERROR_DEFAULTS = {
|
|
|
77
79
|
userMessage: 'The program to run changed on disk after you approved it. Nothing was executed. ' +
|
|
78
80
|
'Re-run the command to review the current content and approve it again.',
|
|
79
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
|
+
},
|
|
80
90
|
};
|
|
81
91
|
export class SepError extends Error {
|
|
82
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:
|
|
207
|
+
pattern: formatPatternSchema.optional(),
|
|
11
208
|
minLength: z.number().int().optional(),
|
|
12
209
|
maxLength: z.number().int().optional(),
|
|
13
210
|
example: z.string().optional(),
|