capjs-core 0.1.6 → 0.1.7

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/src/index.d.ts CHANGED
@@ -6,16 +6,31 @@ export interface ChallengeSpec {
6
6
 
7
7
  export interface InstrumentationOptions {
8
8
  blockAutomatedBrowsers?: boolean;
9
+ detectAutomatedInteractions?: boolean;
10
+ blockAutomatedInteractions?: boolean;
9
11
  obfuscationLevel?: number;
10
12
  ttlMs?: number;
13
+ behavior?: boolean;
14
+ }
15
+
16
+ export interface InstrumentationBinding {
17
+ pv: number[];
18
+ sl: number[];
19
+ hs: [number, number];
20
+ j: [number, number];
11
21
  }
12
22
 
13
23
  export interface GeneratedInstrumentation {
14
24
  id: string;
25
+ iat?: number;
15
26
  expires: number;
16
- expectedVals: number[];
17
27
  vars: string[];
28
+ binding?: InstrumentationBinding;
29
+ expectedVals?: number[];
18
30
  blockAutomatedBrowsers: boolean;
31
+ detectAutomatedInteractions?: boolean;
32
+ blockAutomatedInteractions?: boolean;
33
+ behavior?: boolean;
19
34
  instrumentation: string;
20
35
  }
21
36
 
@@ -47,8 +62,108 @@ export interface RswMinter {
47
62
  mint(): { x_hex: string; y_hex: string };
48
63
  }
49
64
 
65
+ export interface BehaviorOptions {
66
+ block?: boolean;
67
+ threshold?: number;
68
+ strict?: boolean;
69
+ requireInteraction?: boolean;
70
+ blockAgents?: false | "identified" | "all";
71
+ allowVerifiedAgents?: boolean;
72
+ }
73
+
74
+ export interface AgentResult {
75
+ detected: boolean;
76
+ name: string | null;
77
+ confidence: "low" | "medium" | "high";
78
+ verified: boolean;
79
+ evidence: string[];
80
+ }
81
+
82
+ export interface BehaviorTrace {
83
+ v: 1;
84
+ res: number;
85
+ now: number;
86
+ vis: number;
87
+ last: number;
88
+ env: number[];
89
+ n: number[];
90
+ m: [number, number, number, number, number][];
91
+ k: [number, number][];
92
+ a: Record<string, number> | null;
93
+ us: number;
94
+ }
95
+
96
+ export interface BehaviorResult {
97
+ score: number | null;
98
+ verdict:
99
+ | "ok"
100
+ | "no_interaction"
101
+ | "suspicious"
102
+ | "agent"
103
+ | "automated"
104
+ | "missing";
105
+ flags: string[];
106
+ signals: Record<string, unknown>;
107
+ agent?: AgentResult;
108
+ }
109
+
110
+ export function verifyWebBotAuth(
111
+ req: {
112
+ method: string;
113
+ url: string;
114
+ headers: Headers | Record<string, string>;
115
+ },
116
+ opts?: {
117
+ fetch?: typeof fetch;
118
+ now?: number;
119
+ maxSkewSec?: number;
120
+ trustedOrigins?: string[];
121
+ nonceCheck?: (nonce: string, ttlMs: number) => boolean | Promise<boolean>;
122
+ },
123
+ ): Promise<{
124
+ verified: boolean;
125
+ agent?: string;
126
+ origin?: string;
127
+ keyid?: string;
128
+ reason?: string;
129
+ }>;
130
+
131
+ export interface AgentEvidence {
132
+ agent: string | null;
133
+ confidence: "low" | "medium" | "high";
134
+ evidence: string;
135
+ }
136
+
137
+ export function agentFromHeaders(
138
+ headers: Headers | Record<string, string>,
139
+ ): AgentEvidence[];
140
+
141
+ export function agentFromTrace(trace: BehaviorTrace): AgentEvidence[];
142
+
143
+ export function summarizeAgent(
144
+ found: AgentEvidence[],
145
+ verification?: {
146
+ verified: boolean;
147
+ agent?: string;
148
+ origin?: string;
149
+ } | null,
150
+ ): AgentResult | null;
151
+
152
+ export function parseSignatureAgent(
153
+ value: string | null | undefined,
154
+ ): { label: string | null; url: string }[];
155
+
156
+ export const BEHAVIOR_WEIGHTS: Record<string, number>;
157
+ export const DEFAULT_BEHAVIOR_THRESHOLD: number;
158
+ export const WEAK_CAP: number;
159
+
160
+ export function analyzeBehavior(
161
+ trace: unknown,
162
+ ctx?: { replayed?: boolean; strict?: boolean; threshold?: number },
163
+ ): BehaviorResult & { digest: string | null };
164
+
50
165
  export interface GenerateChallengeOptions {
51
- // ── format-1 (default) ──
166
+ behavior?: boolean | BehaviorOptions;
52
167
  challengeCount?: number;
53
168
  challengeSize?: number;
54
169
  challengeDifficulty?: number;
@@ -60,23 +175,14 @@ export interface GenerateChallengeOptions {
60
175
  opts: InstrumentationOptions,
61
176
  ) => Promise<GeneratedInstrumentation>;
62
177
 
63
- // ── format-2 opt-in ──
64
178
  format?: 2;
65
179
  protocols?: ProtocolName[];
66
- /** RSW keypair, either as raw BigInts (from `generateRswKeypair`) or as a
67
- * serialized JSON-friendly form (from `serializeRswKeypair`). cap-core
68
- * builds the minter on first call and memoizes it on this object. */
69
180
  keypair?: RswKeypair | SerializedRswKeypair;
70
- /** Number of sequential squarings the client must perform. Default 75_000. */
71
181
  t?: number;
72
- /** Alternative to `keypair`: pass a pre-built minter (advanced). */
73
182
  rsw?: RswMinter;
74
183
 
75
- /** Expected number of HashWX hashes per solve, across all sub-challenges. Default 1_000_000. */
76
184
  hashwxDifficulty?: number;
77
- /** Nonces covered by each generated hash function. Default 65_536. */
78
185
  hashwxNoncesPerHash?: number;
79
- /** Independent sub-challenges the difficulty is split across. Default 4. */
80
186
  hashwxChallengeCount?: number;
81
187
  }
82
188
 
@@ -90,6 +196,7 @@ export interface Format1ChallengeResult {
90
196
  token: string;
91
197
  expires: number;
92
198
  instrumentation?: string;
199
+ behavior?: true;
93
200
  }
94
201
 
95
202
  export interface Format2ChallengeResult {
@@ -97,6 +204,7 @@ export interface Format2ChallengeResult {
97
204
  format: 2;
98
205
  challenges: Format2ChallengeEntry[];
99
206
  expires: number;
207
+ behavior?: true;
100
208
  }
101
209
 
102
210
  export type ChallengeResult = Format1ChallengeResult | Format2ChallengeResult;
@@ -134,14 +242,60 @@ export interface DetectionResult {
134
242
  riskFlags: string[];
135
243
  }
136
244
 
245
+ export interface BoundData {
246
+ v: 1;
247
+ el: number;
248
+ p: ProbeVector | null;
249
+ vp: [number, number] | null;
250
+ hp: Record<string, unknown> | null;
251
+ ag: string[];
252
+ ia: [number, number] | null;
253
+ bh: BehaviorTrace | null;
254
+ }
255
+
137
256
  export interface InstrumentationPayload {
138
257
  i: string;
139
258
  state: Record<string, number>;
140
- p?: ProbeVector;
141
- vp?: [number, number];
142
- ts?: number;
259
+ d: string;
260
+ }
261
+
262
+ export interface InstrumentationVerdict {
263
+ valid: boolean;
264
+ reason?: string;
265
+ riskFlags?: string[];
266
+ blockedBy?: string[];
267
+ checks?: DetectionCheck[];
268
+ agent?: {
269
+ all: string[];
270
+ host: boolean;
271
+ legacy: boolean;
272
+ isolated: boolean;
273
+ browser: string | null;
274
+ };
275
+ trace?: BehaviorTrace | null;
143
276
  }
144
277
 
278
+ export const MAX_BOUND_DATA: number;
279
+
280
+ export function generateInstrumentation(
281
+ opts?: InstrumentationOptions,
282
+ ): Promise<GeneratedInstrumentation & { binding: InstrumentationBinding }>;
283
+
284
+ export function canonicalize<T>(value: T): T;
285
+
286
+ export function serializeBound(data: BoundData): string;
287
+
288
+ export function boundValues(
289
+ binding: InstrumentationBinding,
290
+ serialized: string,
291
+ ): number[];
292
+
293
+ export function verifyInstrumentationResult(
294
+ meta: GeneratedInstrumentation,
295
+ payload: InstrumentationPayload,
296
+ now?: number,
297
+ ): InstrumentationVerdict;
298
+
145
299
  export interface Format2SolutionShaPow {
146
300
  nonce: number | string;
147
301
  }
@@ -155,6 +309,7 @@ export interface Format2SolutionInstrumentation {
155
309
  instr?: InstrumentationPayload;
156
310
  blocked?: boolean;
157
311
  timeout?: boolean;
312
+ error?: string;
158
313
  }
159
314
  export type Format2Solution =
160
315
  | Format2SolutionShaPow
@@ -164,12 +319,11 @@ export type Format2Solution =
164
319
 
165
320
  export interface ValidateChallengeBody {
166
321
  token: string;
167
- /** Format-1: an array of nonces. Format-2: an array of protocol-specific
168
- * solution objects, in the same order as `challenges`. */
169
322
  solutions: number[] | Format2Solution[];
170
323
  instr?: InstrumentationPayload;
171
324
  instr_blocked?: boolean;
172
325
  instr_timeout?: boolean;
326
+ instr_error?: string;
173
327
  }
174
328
 
175
329
  export interface ValidateChallengeOptions {
@@ -184,6 +338,23 @@ export interface ValidateChallengeOptions {
184
338
  expires: number;
185
339
  iat?: number;
186
340
  }) => Promise<string> | string;
341
+ request?: {
342
+ method: string;
343
+ url: string;
344
+ headers: Headers | Record<string, string>;
345
+ };
346
+ verifyWebBotAuth?: boolean;
347
+ webBotAuthAuthority?: string;
348
+ fetch?: typeof fetch;
349
+ trustedSignatureAgents?: string[];
350
+ webBotAuthNonceCheck?: (
351
+ nonce: string,
352
+ ttlMs: number,
353
+ ) => Promise<boolean> | boolean;
354
+ behaviorReplayCheck?: (
355
+ digest: string,
356
+ ttlMs: number,
357
+ ) => Promise<boolean> | boolean;
187
358
  }
188
359
 
189
360
  export interface ValidateChallengeSuccess {
@@ -193,17 +364,18 @@ export interface ValidateChallengeSuccess {
193
364
  expires: number;
194
365
  scope: string | null;
195
366
  iat?: number;
196
- /** Non-gating automation signals (e.g. `native_tamper`). Raise difficulty, do not block. */
197
367
  riskFlags: string[];
368
+ behavior?: BehaviorResult;
369
+ agent?: AgentResult;
198
370
  }
199
371
 
200
372
  export interface ValidateChallengeFailure {
201
373
  success: false;
202
374
  reason: string;
203
375
  instr_error?: boolean;
204
- /** Ids of the gating detection checks that failed, when reason is `instr_automated_browser`. */
205
376
  blockedBy?: string[];
206
377
  error?: string;
378
+ behavior?: BehaviorResult;
207
379
  }
208
380
 
209
381
  export type ValidateChallengeResult =
@@ -221,25 +393,16 @@ export function validateChallenge(
221
393
  opts?: ValidateChallengeOptions,
222
394
  ): Promise<ValidateChallengeResult>;
223
395
 
224
- // ─── RSW (format-2) helpers ────────────────────────────────────────────────
225
- /** Generates a fresh 2048-bit-by-default RSA-style modulus + factors. Expensive
226
- * (~700 ms to several seconds depending on prime-roll luck). Generate once
227
- * at boot and persist the result. */
228
396
  export function generateRswKeypair(bits?: number): RswKeypair;
229
397
 
230
- /** JSON-friendly form, safe to write to disk or env. */
231
398
  export function serializeRswKeypair(kp: RswKeypair): SerializedRswKeypair;
232
399
  export function deserializeRswKeypair(s: SerializedRswKeypair): RswKeypair;
233
400
 
234
- /** Build a minter for a specific squaring count `t`. Memoized when used via
235
- * `generateChallenge({ keypair, t })`; this direct API is for callers who
236
- * want full control. */
237
401
  export function buildRswMinter(
238
402
  args: { N: bigint; p: bigint; q: bigint; t: number; g?: bigint },
239
403
  opts?: { bits?: number },
240
404
  ): RswMinter;
241
405
 
242
- // ─── HashWX (format-2) helpers ─────────────────────────────────────────────
243
406
  export const DEFAULT_HASHWX_DIFFICULTY: number;
244
407
  export const DEFAULT_HASHWX_NONCES_PER_HASH: number;
245
408
  export const DEFAULT_HASHWX_CHALLENGE_COUNT: number;
@@ -250,36 +413,28 @@ export interface HashwxState {
250
413
  seedPtr: number;
251
414
  }
252
415
 
253
- /** Supplies a precompiled HashWX module for runtimes that forbid compiling
254
- * wasm at runtime. Cloudflare Workers pick up
255
- * the bundled `hashwx.wasm` automatically. Must be called before the first
256
- * verification or `hashwxReady()`. */
257
416
  export function setHashwxModule(module: WebAssembly.Module): void;
258
417
 
259
- /** Compiles and initializes the embedded HashWX wasm. Called automatically on
260
- * first verification; await it at boot to move the cost off the first
261
- * request. Resolves to a state handle reusable with `hashwxHash`. */
262
418
  export function hashwxReady(): Promise<HashwxState>;
263
419
 
264
- /** Largest 64-bit hash accepted at the given difficulty. */
265
420
  export function hashwxTarget(difficulty: number | bigint): bigint;
266
421
 
267
- /** sha256(challenge || u64le(block)), the seed for one generated hash
268
- * function. Each seed covers `hashwxNoncesPerHash` consecutive nonces. */
269
422
  export function hashwxSeed(
270
423
  challenge: Uint8Array,
271
424
  block: number | bigint,
272
425
  ): Uint8Array;
273
426
 
274
- /** Generates the hash function for `seed` and runs it over `nonce`. Returns an
275
- * unsigned 64-bit value; a solution needs it at or below `hashwxTarget(d)`. */
276
427
  export function hashwxHash(
277
428
  state: HashwxState,
278
429
  seed: Uint8Array,
279
430
  nonce: bigint,
280
431
  ): bigint;
281
432
 
282
- /** Pure server-side automation detector over the probe vector the
283
- * instrumentation script ships back. Exposed for testing and for callers
284
- * that run their own instrumentation pipeline. */
285
- export function detectAutomation(vector: ProbeVector): DetectionResult;
433
+ export interface DetectAutomationOptions {
434
+ fontStackCount?: number;
435
+ }
436
+
437
+ export function detectAutomation(
438
+ vector: ProbeVector,
439
+ opts?: DetectAutomationOptions,
440
+ ): DetectionResult;