@nuetzliches/croniq-runner 0.3.0 → 0.4.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.
- package/CHANGELOG.md +63 -0
- package/README.md +25 -2
- package/dist/client.d.ts +61 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +87 -0
- package/dist/client.js.map +1 -1
- package/dist/dispatcher.d.ts +1 -1
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +34 -7
- package/dist/dispatcher.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/log-writer.d.ts +1 -1
- package/dist/log-writer.d.ts.map +1 -1
- package/dist/log-writer.js +17 -3
- package/dist/log-writer.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -2
- package/dist/logger.js.map +1 -1
- package/dist/options.d.ts +42 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +21 -1
- package/dist/options.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +145 -6
- package/dist/runner.js.map +1 -1
- package/dist/sanitize.d.ts +82 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +157 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/security.d.ts +19 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +83 -0
- package/dist/security.js.map +1 -0
- package/dist/trigger.d.ts +21 -1
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +4 -0
- package/dist/trigger.js.map +1 -1
- package/package.json +1 -1
package/dist/options.d.ts
CHANGED
|
@@ -19,8 +19,22 @@ export interface ResolvedLogWriterOptions {
|
|
|
19
19
|
shutdownTimeoutMs: number;
|
|
20
20
|
}
|
|
21
21
|
export interface CroniqRunnerOptions {
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Base URL of the Croniq server, e.g. `http://localhost:4000`.
|
|
24
|
+
*
|
|
25
|
+
* `https://` is required unless the host is loopback (`localhost`,
|
|
26
|
+
* `127.0.0.0/8`, `::1`) — the API key rides along on every request and would
|
|
27
|
+
* otherwise travel in cleartext. See {@link allowInsecureHttp}.
|
|
28
|
+
*/
|
|
23
29
|
serverUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Opt in to a cleartext `http://` {@link serverUrl} on a non-loopback host.
|
|
32
|
+
*
|
|
33
|
+
* Off by default: without it such a URL is rejected by `resolveOptions`.
|
|
34
|
+
* With it, the SDK still emits one loud startup warning — the API key then
|
|
35
|
+
* travels in cleartext on every poll. Default `false`.
|
|
36
|
+
*/
|
|
37
|
+
allowInsecureHttp?: boolean;
|
|
24
38
|
/**
|
|
25
39
|
* Stable runner identifier. If omitted, the SDK resolves it via
|
|
26
40
|
* `RUNNER_ID` env var → state file under {@link runnerDataDir} →
|
|
@@ -62,6 +76,30 @@ export interface CroniqRunnerOptions {
|
|
|
62
76
|
pollRetryDelayMs?: number;
|
|
63
77
|
/** Idle delay when the runner is at `maxInflight` capacity. Default 500 ms. */
|
|
64
78
|
capacityBackoffMs?: number;
|
|
79
|
+
/**
|
|
80
|
+
* How many consecutive `409 Conflict` responses from `POST /v1/work/poll`
|
|
81
|
+
* the runner tolerates before `run()` rejects with a
|
|
82
|
+
* `PollInstanceConflictError`. Default 3, range [1, 100].
|
|
83
|
+
*
|
|
84
|
+
* A sustained 409 means a second process is registered under the same
|
|
85
|
+
* `runnerId` and no amount of retrying fixes that. The counter resets on a
|
|
86
|
+
* successful poll or on any non-409 failure (5xx, network, timeout), which
|
|
87
|
+
* say nothing about instance ownership.
|
|
88
|
+
*/
|
|
89
|
+
maxConsecutivePollConflicts?: number;
|
|
90
|
+
/**
|
|
91
|
+
* How many consecutive `401 Unauthorized` responses from
|
|
92
|
+
* `POST /v1/work/poll` the runner tolerates before `run()` rejects with an
|
|
93
|
+
* `AuthFailedError`. Default 3, range [1, 100].
|
|
94
|
+
*
|
|
95
|
+
* The API key is read once and never re-read, so a rejected credential
|
|
96
|
+
* cannot fix itself; retrying only produces an idle-looking process that
|
|
97
|
+
* never exits. Not fatal on the first 401 — rotation hands over through an
|
|
98
|
+
* expiry window (server issue #471) and a race around it should not kill a
|
|
99
|
+
* healthy runner. The counter resets on a successful poll and on any other
|
|
100
|
+
* failure: a 5xx says nothing about whether the key is valid.
|
|
101
|
+
*/
|
|
102
|
+
maxConsecutiveAuthFailures?: number;
|
|
65
103
|
/** Streaming log-writer tunables. */
|
|
66
104
|
logWriter?: LogWriterOptions;
|
|
67
105
|
/** Optional logger for SDK-level diagnostics. Defaults to a console logger that emits warn/error only. */
|
|
@@ -69,6 +107,7 @@ export interface CroniqRunnerOptions {
|
|
|
69
107
|
}
|
|
70
108
|
export interface ResolvedRunnerOptions {
|
|
71
109
|
serverUrl: string;
|
|
110
|
+
allowInsecureHttp: boolean;
|
|
72
111
|
runnerId: string | undefined;
|
|
73
112
|
runnerIdPrefix: string;
|
|
74
113
|
runnerDataDir: string | undefined;
|
|
@@ -82,6 +121,8 @@ export interface ResolvedRunnerOptions {
|
|
|
82
121
|
drainTimeoutMs: number;
|
|
83
122
|
pollRetryDelayMs: number;
|
|
84
123
|
capacityBackoffMs: number;
|
|
124
|
+
maxConsecutivePollConflicts: number;
|
|
125
|
+
maxConsecutiveAuthFailures: number;
|
|
85
126
|
logWriter: ResolvedLogWriterOptions;
|
|
86
127
|
logger: Logger;
|
|
87
128
|
}
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
package/dist/options.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertSecureServerUrl } from './security.js';
|
|
1
2
|
import { trimTrailingSlashes } from './url.js';
|
|
2
3
|
const DEFAULT_LOG_WRITER = {
|
|
3
4
|
channelCapacity: 256,
|
|
@@ -17,10 +18,26 @@ export function resolveOptions(input, defaultLogger) {
|
|
|
17
18
|
catch {
|
|
18
19
|
throw new TypeError(`CroniqRunnerOptions.serverUrl is not a valid URL: ${input.serverUrl}`);
|
|
19
20
|
}
|
|
21
|
+
const logger = input.logger ?? defaultLogger;
|
|
22
|
+
// Fail fast on a cleartext base URL that would leak the API key (#440).
|
|
23
|
+
const allowInsecureHttp = input.allowInsecureHttp ?? false;
|
|
24
|
+
assertSecureServerUrl(input.serverUrl, allowInsecureHttp, 'CroniqRunnerOptions.serverUrl', logger);
|
|
20
25
|
const maxInflight = input.maxInflight ?? 5;
|
|
21
26
|
if (!Number.isInteger(maxInflight) || maxInflight < 1 || maxInflight > 1024) {
|
|
22
27
|
throw new RangeError(`CroniqRunnerOptions.maxInflight must be an integer in [1, 1024], got ${maxInflight}`);
|
|
23
28
|
}
|
|
29
|
+
const maxConsecutivePollConflicts = input.maxConsecutivePollConflicts ?? 3;
|
|
30
|
+
if (!Number.isInteger(maxConsecutivePollConflicts) ||
|
|
31
|
+
maxConsecutivePollConflicts < 1 ||
|
|
32
|
+
maxConsecutivePollConflicts > 100) {
|
|
33
|
+
throw new RangeError(`CroniqRunnerOptions.maxConsecutivePollConflicts must be an integer in [1, 100], got ${maxConsecutivePollConflicts}`);
|
|
34
|
+
}
|
|
35
|
+
const maxConsecutiveAuthFailures = input.maxConsecutiveAuthFailures ?? 3;
|
|
36
|
+
if (!Number.isInteger(maxConsecutiveAuthFailures) ||
|
|
37
|
+
maxConsecutiveAuthFailures < 1 ||
|
|
38
|
+
maxConsecutiveAuthFailures > 100) {
|
|
39
|
+
throw new RangeError(`CroniqRunnerOptions.maxConsecutiveAuthFailures must be an integer in [1, 100], got ${maxConsecutiveAuthFailures}`);
|
|
40
|
+
}
|
|
24
41
|
const logWriter = {
|
|
25
42
|
channelCapacity: input.logWriter?.channelCapacity ?? DEFAULT_LOG_WRITER.channelCapacity,
|
|
26
43
|
batchSizeThreshold: input.logWriter?.batchSizeThreshold ?? DEFAULT_LOG_WRITER.batchSizeThreshold,
|
|
@@ -30,6 +47,7 @@ export function resolveOptions(input, defaultLogger) {
|
|
|
30
47
|
};
|
|
31
48
|
return {
|
|
32
49
|
serverUrl: trimTrailingSlashes(input.serverUrl),
|
|
50
|
+
allowInsecureHttp,
|
|
33
51
|
runnerId: input.runnerId,
|
|
34
52
|
runnerIdPrefix: input.runnerIdPrefix ?? 'runner',
|
|
35
53
|
runnerDataDir: input.runnerDataDir,
|
|
@@ -43,8 +61,10 @@ export function resolveOptions(input, defaultLogger) {
|
|
|
43
61
|
drainTimeoutMs: input.drainTimeoutMs ?? 30_000,
|
|
44
62
|
pollRetryDelayMs: input.pollRetryDelayMs ?? 5_000,
|
|
45
63
|
capacityBackoffMs: input.capacityBackoffMs ?? 500,
|
|
64
|
+
maxConsecutivePollConflicts,
|
|
65
|
+
maxConsecutiveAuthFailures,
|
|
46
66
|
logWriter,
|
|
47
|
-
logger
|
|
67
|
+
logger,
|
|
48
68
|
};
|
|
49
69
|
}
|
|
50
70
|
//# sourceMappingURL=options.js.map
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAkB,KAAK,mBAAmB,EAA8B,MAAM,cAAc,CAAC;AAepG;;;;;;;GAOG;AACH,qBAAa,YAAY;;gBAaX,OAAO,EAAE,mBAAmB;IAYxC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAKhF,gFAAgF;IAChF,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;IAK5C,qEAAqE;IACrE,IAAI,QAAQ,IAAI,MAAM,CAGrB;IAED,sEAAsE;IACtE,IAAI,QAAQ,IAAI,SAAS,MAAM,EAAE,CAEhC;IAED;;;;OAIG;IACG,GAAG,CAAC,MAAM,GAAE,WAA0C,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C5E;;;OAGG;IACH,YAAY,IAAI,IAAI;CA0RrB;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAEvE"}
|
package/dist/runner.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { setMaxListeners } from 'node:events';
|
|
3
3
|
import { anySignal } from './abort.js';
|
|
4
|
-
import { CroniqClient, HttpError } from './client.js';
|
|
4
|
+
import { CroniqClient, HttpError, AuthFailedError, PollInstanceConflictError, RunnerOwnershipDeniedError, isInstanceConflict, isOwnershipDenied, isUnauthorized, } from './client.js';
|
|
5
5
|
import { sleep } from './deferred.js';
|
|
6
6
|
import { ExecutionDispatcher } from './dispatcher.js';
|
|
7
7
|
import { HandlerRegistry, } from './handler.js';
|
|
8
8
|
import { resolveRunnerId } from './identity.js';
|
|
9
9
|
import { consoleLogger } from './logger.js';
|
|
10
10
|
import { resolveOptions } from './options.js';
|
|
11
|
+
import { isSafeExecutionId, previewForLog, rejectAssignmentReason, rejectionAckError, } from './sanitize.js';
|
|
11
12
|
/**
|
|
12
13
|
* The Croniq runner: polls the server for work, dispatches handlers
|
|
13
14
|
* concurrently up to {@link CroniqRunnerOptions.maxInflight}, renews leases
|
|
@@ -134,6 +135,13 @@ export class CroniqRunner {
|
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
async #pollLoop(signal) {
|
|
138
|
+
// Consecutive 409 Conflict responses on poll. Reset by a successful poll
|
|
139
|
+
// or by any non-409 failure — see `maxConsecutivePollConflicts`.
|
|
140
|
+
let consecutiveConflicts = 0;
|
|
141
|
+
// Consecutive 401s, tracked separately: a run of conflicts must not
|
|
142
|
+
// spend the auth budget, or a duplicate deployment would be reported
|
|
143
|
+
// as an authentication failure.
|
|
144
|
+
let consecutiveAuthFailures = 0;
|
|
137
145
|
while (!signal.aborted) {
|
|
138
146
|
// Control-slot polling (issue #176): even at capacity we still poll
|
|
139
147
|
// so the server can deliver cancels via PollResponse.cancel. The
|
|
@@ -155,10 +163,74 @@ export class CroniqRunner {
|
|
|
155
163
|
catch (err) {
|
|
156
164
|
if (signal.aborted)
|
|
157
165
|
return;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
// A 403 is permanent (issue #437): the credential is bound to
|
|
167
|
+
// another runner_id, so the next poll fails identically. Stop with
|
|
168
|
+
// an actionable error instead of retrying on the poll interval,
|
|
169
|
+
// which makes a fenced-out runner look merely idle.
|
|
170
|
+
if (isOwnershipDenied(err)) {
|
|
171
|
+
this.#logger.error('fatal: poll refused with 403 Forbidden — this runner\'s credential does not own ' +
|
|
172
|
+
'runner_id. Give the runner its own runner_id, or release the existing binding ' +
|
|
173
|
+
'with DELETE /v1/runners/{id}', { runner_id: this.#runnerId });
|
|
174
|
+
throw new RunnerOwnershipDeniedError(this.#runnerId);
|
|
175
|
+
}
|
|
176
|
+
// A 401 says the key was rejected, and the client never re-reads
|
|
177
|
+
// it, so every later poll presents the same dead credential.
|
|
178
|
+
// Budgeted rather than fatal at once: rotation hands over through an
|
|
179
|
+
// expiry window (server issue #471) and a race around it should not
|
|
180
|
+
// kill a healthy runner (issue #473).
|
|
181
|
+
if (isUnauthorized(err)) {
|
|
182
|
+
consecutiveAuthFailures += 1;
|
|
183
|
+
if (consecutiveAuthFailures >= this.#options.maxConsecutiveAuthFailures) {
|
|
184
|
+
this.#logger.error(`fatal: poll refused with 401 Unauthorized ${consecutiveAuthFailures} times in ` +
|
|
185
|
+
'a row — the API key was rejected. It may have been revoked, or its rotation ' +
|
|
186
|
+
'grace window may have elapsed. Restart the runner with the current key', { runner_id: this.#runnerId });
|
|
187
|
+
throw new AuthFailedError(consecutiveAuthFailures);
|
|
188
|
+
}
|
|
189
|
+
this.#logger.warn(`poll returned 401 Unauthorized (${consecutiveAuthFailures}/` +
|
|
190
|
+
`${this.#options.maxConsecutiveAuthFailures}) — the API key was rejected; ` +
|
|
191
|
+
`retrying after ${this.#options.pollRetryDelayMs}ms`, { runner_id: this.#runnerId });
|
|
192
|
+
// A 401 is not a 409, so it clears the conflict budget just like
|
|
193
|
+
// any other non-409 failure. This branch returns before reaching
|
|
194
|
+
// the reset below, so it has to do it here (issue #508).
|
|
195
|
+
consecutiveConflicts = 0;
|
|
196
|
+
try {
|
|
197
|
+
await sleep(this.#options.pollRetryDelayMs, signal);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
// Anything that is not a 401 clears the auth budget: a 5xx or a
|
|
205
|
+
// timeout says nothing about whether the credential is valid.
|
|
206
|
+
consecutiveAuthFailures = 0;
|
|
207
|
+
// A 409 means a newer instance has taken this runner_id over
|
|
208
|
+
// (fencing, issue #374). One is transient — the deposed instance may
|
|
209
|
+
// win it back — so we back off and retry. A streak of them is a
|
|
210
|
+
// duplicate deployment, and retrying forever hides it behind a
|
|
211
|
+
// warning that scrolls past (issue #134 sub-item 1).
|
|
212
|
+
if (isInstanceConflict(err)) {
|
|
213
|
+
consecutiveConflicts += 1;
|
|
214
|
+
if (consecutiveConflicts >= this.#options.maxConsecutivePollConflicts) {
|
|
215
|
+
this.#logger.error(`fatal: poll refused with 409 Conflict ${consecutiveConflicts} times in a row — ` +
|
|
216
|
+
'another runner is registered with this runner_id. Stop the duplicate process ' +
|
|
217
|
+
'or rotate the runner_id', { runner_id: this.#runnerId });
|
|
218
|
+
throw new PollInstanceConflictError(this.#runnerId, consecutiveConflicts);
|
|
219
|
+
}
|
|
220
|
+
this.#logger.warn(`poll returned 409 Conflict (${consecutiveConflicts}/` +
|
|
221
|
+
`${this.#options.maxConsecutivePollConflicts}) — another runner instance may be ` +
|
|
222
|
+
`active; retrying after ${this.#options.pollRetryDelayMs}ms`, { runner_id: this.#runnerId });
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
// Non-409 transient (5xx, network, timeout) — unrelated to instance
|
|
226
|
+
// ownership, so a recovered outage must not accumulate with later
|
|
227
|
+
// conflicts.
|
|
228
|
+
consecutiveConflicts = 0;
|
|
229
|
+
const detail = err instanceof HttpError
|
|
230
|
+
? { status: err.status, status_text: err.statusText }
|
|
231
|
+
: { error: String(err) };
|
|
232
|
+
this.#logger.warn(`poll failed — backing off ${this.#options.pollRetryDelayMs}ms`, detail);
|
|
233
|
+
}
|
|
162
234
|
try {
|
|
163
235
|
await sleep(this.#options.pollRetryDelayMs, signal);
|
|
164
236
|
}
|
|
@@ -167,6 +239,13 @@ export class CroniqRunner {
|
|
|
167
239
|
}
|
|
168
240
|
continue;
|
|
169
241
|
}
|
|
242
|
+
// Poll succeeded — the other instance must have died or released the
|
|
243
|
+
// identity, so the conflict streak starts over. The auth budget starts
|
|
244
|
+
// over with it: the credential just worked, so an earlier 401 must not
|
|
245
|
+
// still count against a runner that has been healthy since (issue
|
|
246
|
+
// #507).
|
|
247
|
+
consecutiveConflicts = 0;
|
|
248
|
+
consecutiveAuthFailures = 0;
|
|
170
249
|
this.#handleCancellations(response.cancel);
|
|
171
250
|
if (atCapacity) {
|
|
172
251
|
// Work is always empty in this branch (server-side capacity
|
|
@@ -180,6 +259,14 @@ export class CroniqRunner {
|
|
|
180
259
|
continue;
|
|
181
260
|
}
|
|
182
261
|
for (const assignment of response.work) {
|
|
262
|
+
// Ingest guard: an assignment carrying a control character in either
|
|
263
|
+
// identifier never reaches a handler, a log record or a telemetry
|
|
264
|
+
// attribute. See sanitize.ts for the rule and why it is a denylist.
|
|
265
|
+
const rejected = rejectAssignmentReason(assignment.execution_id, assignment.job_key);
|
|
266
|
+
if (rejected !== undefined) {
|
|
267
|
+
await this.#rejectAssignment(assignment, rejected, signal);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
183
270
|
if (this.#inflight.has(assignment.execution_id))
|
|
184
271
|
continue;
|
|
185
272
|
const ac = new AbortController();
|
|
@@ -193,13 +280,65 @@ export class CroniqRunner {
|
|
|
193
280
|
}
|
|
194
281
|
}
|
|
195
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Handle a work assignment refused by the ingest guard.
|
|
285
|
+
*
|
|
286
|
+
* The two cases differ in what the runner can still tell the server:
|
|
287
|
+
*
|
|
288
|
+
* - **Unsafe `execution_id`** — nothing. That value is what addresses an ack
|
|
289
|
+
* or renew, so there is no way to report anything about this execution.
|
|
290
|
+
* The assignment is dropped and the server's lease expires.
|
|
291
|
+
* - **Unsafe `job_key`, valid `execution_id`** — a failure ack. The handler
|
|
292
|
+
* never runs, but the execution completes with an error naming the
|
|
293
|
+
* offending field, so the operator sees a dead-lettered execution instead
|
|
294
|
+
* of an execution that is silently requeued by the stale-claim reaper and
|
|
295
|
+
* refused again on every later poll.
|
|
296
|
+
*
|
|
297
|
+
* Awaited rather than fire-and-forget: this path only triggers on malformed
|
|
298
|
+
* input, so pausing the loop for one small POST costs nothing and keeps the
|
|
299
|
+
* ordering observable.
|
|
300
|
+
*/
|
|
301
|
+
async #rejectAssignment(assignment, field, signal) {
|
|
302
|
+
// Escaped and truncated explicitly: this is the one place a refused value
|
|
303
|
+
// is rendered, and it is hostile by definition.
|
|
304
|
+
const preview = previewForLog(assignment[field]);
|
|
305
|
+
const ackable = field === 'job_key';
|
|
306
|
+
this.#logger.warn('rejected work assignment with unsafe identifier', {
|
|
307
|
+
field,
|
|
308
|
+
value: preview,
|
|
309
|
+
acked: ackable,
|
|
310
|
+
});
|
|
311
|
+
if (!ackable)
|
|
312
|
+
return;
|
|
313
|
+
try {
|
|
314
|
+
await this.#client.ack({
|
|
315
|
+
runner_id: this.#runnerId,
|
|
316
|
+
execution_id: assignment.execution_id,
|
|
317
|
+
status: 'failure',
|
|
318
|
+
attempt: assignment.attempt,
|
|
319
|
+
duration_ms: 0,
|
|
320
|
+
error: rejectionAckError(field, assignment[field]),
|
|
321
|
+
}, signal);
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
this.#logger.warn('failed to ack a rejected work assignment', {
|
|
325
|
+
execution_id: assignment.execution_id,
|
|
326
|
+
error: String(err),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
196
330
|
#handleCancellations(cancelIds) {
|
|
197
331
|
if (cancelIds.length === 0)
|
|
198
332
|
return;
|
|
199
333
|
for (const id of cancelIds) {
|
|
334
|
+
// Cancel ids are server-supplied too. An unsafe one can never match an
|
|
335
|
+
// in-flight key (those were validated on ingest), but checking here
|
|
336
|
+
// keeps the value out of the log line below on any code path.
|
|
337
|
+
if (!isSafeExecutionId(id))
|
|
338
|
+
continue;
|
|
200
339
|
const ac = this.#inflight.get(id);
|
|
201
340
|
if (ac && !ac.signal.aborted) {
|
|
202
|
-
this.#logger.info(
|
|
341
|
+
this.#logger.info('server requested cancellation', { execution_id: id });
|
|
203
342
|
ac.abort();
|
|
204
343
|
}
|
|
205
344
|
}
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hygiene for server-supplied strings.
|
|
3
|
+
*
|
|
4
|
+
* `job_key` and `execution_id` arrive from the Croniq server and are echoed
|
|
5
|
+
* into logs and telemetry. The threat actor is a malicious or compromised
|
|
6
|
+
* server — but not only: in a multi-tenant deployment anyone who can name a
|
|
7
|
+
* job key in the Croniqfile controls a string that round-trips to every
|
|
8
|
+
* runner unchanged. A value carrying CRLF forges log records; one carrying
|
|
9
|
+
* ANSI escapes repaints the operator's terminal.
|
|
10
|
+
*
|
|
11
|
+
* Two layers defend against that:
|
|
12
|
+
*
|
|
13
|
+
* 1. {@link assertSafeAssignmentIdentifiers} rejects an assignment whose
|
|
14
|
+
* identifiers fall outside the shape Croniq itself defines, so hostile
|
|
15
|
+
* values never enter the SDK.
|
|
16
|
+
* 2. {@link escapeControlChars} escapes anything a terminal would interpret,
|
|
17
|
+
* applied wherever this SDK renders text itself (see `logger.ts`).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Maximum accepted `job_key` length, counted in Unicode scalar values.
|
|
21
|
+
*
|
|
22
|
+
* The server stores job keys in an unbounded `TEXT` column
|
|
23
|
+
* (`crates/croniq-store/src/migrations/001_initial.sql`), so this bound is the
|
|
24
|
+
* SDK's own. 256 is far above any plausible `namespace:name:variant` while
|
|
25
|
+
* still bounding what a single log line can be made to hold.
|
|
26
|
+
*/
|
|
27
|
+
export declare const MAX_JOB_KEY_LENGTH = 256;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum accepted `execution_id` length. The server always emits a v4 UUID
|
|
30
|
+
* (36 characters); 64 leaves room for the shorter opaque ids used by mock
|
|
31
|
+
* servers and the conformance suite.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_EXECUTION_ID_LENGTH = 64;
|
|
34
|
+
/**
|
|
35
|
+
* Escape every character a terminal interprets rather than prints: the C0
|
|
36
|
+
* range (including ESC, `0x1B`, which introduces every ANSI sequence), DEL,
|
|
37
|
+
* and the C1 range.
|
|
38
|
+
*
|
|
39
|
+
* This mirrors what Go's `slog` handlers do for attribute values, and is
|
|
40
|
+
* applied here because this SDK's console logger writes the message itself.
|
|
41
|
+
* Field maps do not need it — they are rendered with `JSON.stringify`, which
|
|
42
|
+
* already escapes these.
|
|
43
|
+
*/
|
|
44
|
+
export declare function escapeControlChars(value: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Render a rejected value for a diagnostic: escaped so it cannot forge a
|
|
47
|
+
* record, and truncated so an over-long value cannot flood the log either.
|
|
48
|
+
*/
|
|
49
|
+
export declare function previewForLog(value: unknown, maxLength?: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* True when `value` is a job key this runner is willing to act on: non-empty,
|
|
52
|
+
* within {@link MAX_JOB_KEY_LENGTH} scalar values, and free of control
|
|
53
|
+
* characters. Iteration is over scalar values (`for…of`), not UTF-16 code
|
|
54
|
+
* units, so an astral character counts once and a surrogate pair is never
|
|
55
|
+
* inspected half at a time.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isSafeJobKey(value: unknown): value is string;
|
|
58
|
+
/** True when `value` is an execution id this runner is willing to act on. */
|
|
59
|
+
export declare function isSafeExecutionId(value: unknown): value is string;
|
|
60
|
+
/** The identifier that makes a work assignment unacceptable. */
|
|
61
|
+
export type RejectedField = 'execution_id' | 'job_key';
|
|
62
|
+
/**
|
|
63
|
+
* Names the identifier that makes a work assignment unacceptable, or
|
|
64
|
+
* `undefined` when both pass. Only the field name is returned — the offending
|
|
65
|
+
* value is reported separately, escaped, so the diagnostic cannot itself become
|
|
66
|
+
* the injection vector.
|
|
67
|
+
*
|
|
68
|
+
* The two outcomes are handled differently by the caller, and the order here
|
|
69
|
+
* is what makes that possible. An unsafe `execution_id` leaves nothing to
|
|
70
|
+
* address the server with, so the assignment is dropped silently. An unsafe
|
|
71
|
+
* `job_key` with a *valid* `execution_id` can still be acked as a failure, so
|
|
72
|
+
* the operator gets a dead-lettered execution naming the problem instead of a
|
|
73
|
+
* silent requeue loop.
|
|
74
|
+
*/
|
|
75
|
+
export declare function rejectAssignmentReason(executionId: unknown, jobKey: unknown): RejectedField | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* The `error` string acked for an assignment rejected on `job_key`. Names the
|
|
78
|
+
* field and shows the offending value escaped, so the dead-letter row explains
|
|
79
|
+
* itself without carrying a live payload.
|
|
80
|
+
*/
|
|
81
|
+
export declare function rejectionAckError(field: RejectedField, value: unknown): string;
|
|
82
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAkC1C;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,SAAM,GAAG,MAAM,CASrE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAQ5D;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAOjE;AAED,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,SAAS,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,GACd,aAAa,GAAG,SAAS,CAI3B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAE9E"}
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hygiene for server-supplied strings.
|
|
3
|
+
*
|
|
4
|
+
* `job_key` and `execution_id` arrive from the Croniq server and are echoed
|
|
5
|
+
* into logs and telemetry. The threat actor is a malicious or compromised
|
|
6
|
+
* server — but not only: in a multi-tenant deployment anyone who can name a
|
|
7
|
+
* job key in the Croniqfile controls a string that round-trips to every
|
|
8
|
+
* runner unchanged. A value carrying CRLF forges log records; one carrying
|
|
9
|
+
* ANSI escapes repaints the operator's terminal.
|
|
10
|
+
*
|
|
11
|
+
* Two layers defend against that:
|
|
12
|
+
*
|
|
13
|
+
* 1. {@link assertSafeAssignmentIdentifiers} rejects an assignment whose
|
|
14
|
+
* identifiers fall outside the shape Croniq itself defines, so hostile
|
|
15
|
+
* values never enter the SDK.
|
|
16
|
+
* 2. {@link escapeControlChars} escapes anything a terminal would interpret,
|
|
17
|
+
* applied wherever this SDK renders text itself (see `logger.ts`).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Maximum accepted `job_key` length, counted in Unicode scalar values.
|
|
21
|
+
*
|
|
22
|
+
* The server stores job keys in an unbounded `TEXT` column
|
|
23
|
+
* (`crates/croniq-store/src/migrations/001_initial.sql`), so this bound is the
|
|
24
|
+
* SDK's own. 256 is far above any plausible `namespace:name:variant` while
|
|
25
|
+
* still bounding what a single log line can be made to hold.
|
|
26
|
+
*/
|
|
27
|
+
export const MAX_JOB_KEY_LENGTH = 256;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum accepted `execution_id` length. The server always emits a v4 UUID
|
|
30
|
+
* (36 characters); 64 leaves room for the shorter opaque ids used by mock
|
|
31
|
+
* servers and the conformance suite.
|
|
32
|
+
*/
|
|
33
|
+
export const MAX_EXECUTION_ID_LENGTH = 64;
|
|
34
|
+
/**
|
|
35
|
+
* Whether `codePoint` is one a terminal interprets rather than prints: the C0
|
|
36
|
+
* range (`U+0000`–`U+001F`, covering NUL, CR, LF and the ESC that introduces
|
|
37
|
+
* every ANSI sequence), DEL (`U+007F`), and the C1 range
|
|
38
|
+
* (`U+0080`–`U+009F`).
|
|
39
|
+
*
|
|
40
|
+
* This is the rule for `job_key`, and it is a *denylist* on purpose. An
|
|
41
|
+
* allowlist — say, the character set `Lexer::is_ident_char` accepts for an
|
|
42
|
+
* unquoted key in `crates/croniq-config/src/lexer.rs` — would reject keys a
|
|
43
|
+
* legitimate server can issue: `parse_job_key` (`parser.rs:687-717`) also
|
|
44
|
+
* accepts a `QuotedString` and then enforces only the "two or three
|
|
45
|
+
* colon-separated parts" rule, so `job "billing:monthly invoice" { … }` is
|
|
46
|
+
* legal DSL today, and `POST /v1/jobs` constrains the key not at all. Dropping
|
|
47
|
+
* such an assignment would strand a valid configuration.
|
|
48
|
+
*
|
|
49
|
+
* Rejecting the control classes instead blocks precisely the log-forgery and
|
|
50
|
+
* ANSI-injection payloads without touching anything legitimate. An interior
|
|
51
|
+
* space is accepted; so is any other printable scalar value, in any script.
|
|
52
|
+
*/
|
|
53
|
+
function isControlScalar(codePoint) {
|
|
54
|
+
return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Accepted `execution_id` shape. The server generates these as v4 UUIDs
|
|
58
|
+
* (`Uuid::new_v4()`), which is a strict subset of this pattern; the pattern is
|
|
59
|
+
* kept slightly wider so opaque ids from mock servers and older builds still
|
|
60
|
+
* round-trip. What it excludes is what matters: control characters, ESC,
|
|
61
|
+
* whitespace, and anything else a terminal or log parser reacts to.
|
|
62
|
+
*/
|
|
63
|
+
const EXECUTION_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
64
|
+
/**
|
|
65
|
+
* Escape every character a terminal interprets rather than prints: the C0
|
|
66
|
+
* range (including ESC, `0x1B`, which introduces every ANSI sequence), DEL,
|
|
67
|
+
* and the C1 range.
|
|
68
|
+
*
|
|
69
|
+
* This mirrors what Go's `slog` handlers do for attribute values, and is
|
|
70
|
+
* applied here because this SDK's console logger writes the message itself.
|
|
71
|
+
* Field maps do not need it — they are rendered with `JSON.stringify`, which
|
|
72
|
+
* already escapes these.
|
|
73
|
+
*/
|
|
74
|
+
export function escapeControlChars(value) {
|
|
75
|
+
// eslint-disable-next-line no-control-regex
|
|
76
|
+
return value.replace(/[\u0000-\u001F\u007F-\u009F]/g, (ch) => {
|
|
77
|
+
switch (ch) {
|
|
78
|
+
case '\n':
|
|
79
|
+
return '\\n';
|
|
80
|
+
case '\r':
|
|
81
|
+
return '\\r';
|
|
82
|
+
case '\t':
|
|
83
|
+
return '\\t';
|
|
84
|
+
default:
|
|
85
|
+
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Render a rejected value for a diagnostic: escaped so it cannot forge a
|
|
91
|
+
* record, and truncated so an over-long value cannot flood the log either.
|
|
92
|
+
*/
|
|
93
|
+
export function previewForLog(value, maxLength = 120) {
|
|
94
|
+
const text = typeof value === 'string' ? value : String(value);
|
|
95
|
+
// Slice by scalar values, not UTF-16 code units, so truncation cannot leave
|
|
96
|
+
// a lone surrogate behind.
|
|
97
|
+
const scalars = [...text];
|
|
98
|
+
const clipped = scalars.length > maxLength
|
|
99
|
+
? `${scalars.slice(0, maxLength).join('')}…`
|
|
100
|
+
: text;
|
|
101
|
+
return escapeControlChars(clipped);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* True when `value` is a job key this runner is willing to act on: non-empty,
|
|
105
|
+
* within {@link MAX_JOB_KEY_LENGTH} scalar values, and free of control
|
|
106
|
+
* characters. Iteration is over scalar values (`for…of`), not UTF-16 code
|
|
107
|
+
* units, so an astral character counts once and a surrogate pair is never
|
|
108
|
+
* inspected half at a time.
|
|
109
|
+
*/
|
|
110
|
+
export function isSafeJobKey(value) {
|
|
111
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
112
|
+
return false;
|
|
113
|
+
let scalars = 0;
|
|
114
|
+
for (const ch of value) {
|
|
115
|
+
if (++scalars > MAX_JOB_KEY_LENGTH)
|
|
116
|
+
return false;
|
|
117
|
+
if (isControlScalar(ch.codePointAt(0)))
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
/** True when `value` is an execution id this runner is willing to act on. */
|
|
123
|
+
export function isSafeExecutionId(value) {
|
|
124
|
+
return (typeof value === 'string' &&
|
|
125
|
+
value.length > 0 &&
|
|
126
|
+
value.length <= MAX_EXECUTION_ID_LENGTH &&
|
|
127
|
+
EXECUTION_ID_PATTERN.test(value));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Names the identifier that makes a work assignment unacceptable, or
|
|
131
|
+
* `undefined` when both pass. Only the field name is returned — the offending
|
|
132
|
+
* value is reported separately, escaped, so the diagnostic cannot itself become
|
|
133
|
+
* the injection vector.
|
|
134
|
+
*
|
|
135
|
+
* The two outcomes are handled differently by the caller, and the order here
|
|
136
|
+
* is what makes that possible. An unsafe `execution_id` leaves nothing to
|
|
137
|
+
* address the server with, so the assignment is dropped silently. An unsafe
|
|
138
|
+
* `job_key` with a *valid* `execution_id` can still be acked as a failure, so
|
|
139
|
+
* the operator gets a dead-lettered execution naming the problem instead of a
|
|
140
|
+
* silent requeue loop.
|
|
141
|
+
*/
|
|
142
|
+
export function rejectAssignmentReason(executionId, jobKey) {
|
|
143
|
+
if (!isSafeExecutionId(executionId))
|
|
144
|
+
return 'execution_id';
|
|
145
|
+
if (!isSafeJobKey(jobKey))
|
|
146
|
+
return 'job_key';
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The `error` string acked for an assignment rejected on `job_key`. Names the
|
|
151
|
+
* field and shows the offending value escaped, so the dead-letter row explains
|
|
152
|
+
* itself without carrying a live payload.
|
|
153
|
+
*/
|
|
154
|
+
export function rejectionAckError(field, value) {
|
|
155
|
+
return `rejected by runner: unsafe ${field} ${previewForLog(value)}`;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=sanitize.js.map
|