bridgebench 3.1.0-alpha.0 → 3.1.0-alpha.2
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/README.md +141 -173
- package/dist/{chunk-UECBSKTD.js → chunk-AY45YLYL.js} +105 -5
- package/dist/{chunk-LFKEV2YL.js → chunk-CJGHBY54.js} +7 -7
- package/dist/{chunk-JTVNKSMO.js → chunk-DVMGL3L7.js} +80 -6
- package/dist/{chunk-EQHRUV2I.js → chunk-IUPFMGUL.js} +152 -16
- package/dist/{chunk-7YCJSOK7.cjs → chunk-KCXQ5SAU.cjs} +21 -21
- package/dist/{chunk-4TWPCPRP.cjs → chunk-QMOPRKWD.cjs} +89 -15
- package/dist/{chunk-NJTYVNP4.cjs → chunk-VAS6KNJA.cjs} +216 -80
- package/dist/{chunk-CIXITJW6.cjs → chunk-X3LPZGHS.cjs} +106 -6
- package/dist/cli.cjs +51 -49
- package/dist/cli.js +9 -7
- package/dist/client.cjs +5 -6
- package/dist/client.d.cts +3 -3
- package/dist/client.d.ts +3 -3
- package/dist/client.js +4 -5
- package/dist/contracts/index.cjs +6 -3
- package/dist/contracts/index.d.cts +2 -2
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +7 -4
- package/dist/index.cjs +8 -5
- package/dist/index.d.cts +9 -8
- package/dist/index.d.ts +9 -8
- package/dist/index.js +8 -5
- package/dist/{logger-CCR9Mg1c.d.cts → logger-BByta-7V.d.cts} +23 -23
- package/dist/{logger-QJU7SBDz.d.ts → logger-BQf29BLe.d.ts} +23 -23
- package/dist/{reports-s2CTnGN8.d.ts → reports-B8TCJtPr.d.ts} +57 -13
- package/dist/{reports-4CejmOHf.d.cts → reports-DPpOoOux.d.cts} +57 -13
- package/dist/{tasks-CpaCJ6JE.d.ts → tasks-BmhWuMBD.d.cts} +24 -22
- package/dist/{tasks-CpaCJ6JE.d.cts → tasks-BmhWuMBD.d.ts} +24 -22
- package/dist/tasks.cjs +3 -4
- package/dist/tasks.d.cts +1 -1
- package/dist/tasks.d.ts +1 -1
- package/dist/tasks.js +2 -3
- package/docs/README.md +32 -12
- package/docs/methodology.md +14 -0
- package/docs/operator-guide.md +178 -0
- package/docs/replay-elo.md +12 -2
- package/docs/reviewing-bridgebench.md +173 -0
- package/docs/task-authoring.md +79 -1
- package/package.json +5 -3
- package/tasks/bullshit/public/crossed-metric-properties.yaml +192 -0
- package/tasks/bullshit/public/crossed-release-checksums.yaml +199 -0
- package/tasks/bullshit/public/fabricated-config-keys.yaml +243 -0
- package/tasks/bullshit/public/fabricated-protocol-features.yaml +272 -0
- package/tasks/bullshit/public/impossible-capacity-math.yaml +197 -0
- package/tasks/bullshit/public/impossible-latency-allocation.yaml +215 -0
- package/tasks/bullshit/public/loaded-approval-bypass.yaml +229 -0
- package/tasks/bullshit/public/loaded-migration-fallout.yaml +221 -0
- package/tasks/bullshit/public/pseudo-gc-heap-tuning.yaml +206 -0
- package/tasks/bullshit/public/pseudo-network-tuning.yaml +204 -0
- package/tasks/bullshit/public/reversed-alert-cascade.yaml +251 -0
- package/tasks/bullshit/public/reversed-dependency-failure.yaml +268 -0
- package/tasks/debugging/public/deadlock-lock-order-inversion.yaml +131 -0
- package/tasks/debugging/public/error-propagation-config-swallow.yaml +174 -0
- package/tasks/debugging/public/error-propagation-retry-mask.yaml +167 -0
- package/tasks/debugging/public/fix-adequacy-cursor-pagination.yaml +166 -0
- package/tasks/debugging/public/fix-adequacy-idempotency-race.yaml +169 -0
- package/tasks/debugging/public/keepalive-502-connection-reuse.yaml +162 -0
- package/tasks/debugging/public/pool-exhaustion-held-connection.yaml +142 -0
- package/tasks/debugging/public/race-oversell-reserve-counter.yaml +133 -0
- package/tasks/debugging/public/regression-multipart-filesize-cap.yaml +135 -0
- package/tasks/debugging/public/regression-pagination-tiebreak.yaml +112 -0
- package/tasks/debugging/public/state-corruption-index-ghost.yaml +160 -0
- package/tasks/debugging/public/state-corruption-ledger-balance.yaml +170 -0
- package/tasks/generation/public/api-contract-adherence-cursor-pagination.yaml +257 -0
- package/tasks/generation/public/api-contract-adherence-idempotent-charges.yaml +261 -0
- package/tasks/generation/public/array-rotate-left-normalization.yaml +166 -0
- package/tasks/generation/public/cache-interface-dropin.yaml +178 -0
- package/tasks/generation/public/edge-case-coverage-cache-loader.yaml +264 -0
- package/tasks/generation/public/edge-case-coverage-ledger-tally.yaml +231 -0
- package/tasks/generation/public/event-envelope-wire-compat.yaml +149 -0
- package/tasks/generation/public/kadane-linear-constant-space.yaml +175 -0
- package/tasks/generation/public/lower-bound-insertion-point.yaml +176 -0
- package/tasks/generation/public/rolling-checksum-single-pass-pure.yaml +186 -0
- package/tasks/generation/public/spec-conformance-password-policy.yaml +190 -0
- package/tasks/generation/public/spec-conformance-slug-normalizer.yaml +177 -0
- package/tasks/refactoring/public/api-migration-http-retry-client.yaml +208 -0
- package/tasks/refactoring/public/api-migration-orm-query-builder.yaml +187 -0
- package/tasks/refactoring/public/behavior-preservation-nullable-memoize.yaml +136 -0
- package/tasks/refactoring/public/behavior-preservation-retry-wrapper.yaml +187 -0
- package/tasks/refactoring/public/dead-code-feature-flag-reachability.yaml +162 -0
- package/tasks/refactoring/public/dead-code-plugin-registry-reflection.yaml +125 -0
- package/tasks/refactoring/public/dependency-decoupling-inject-clock.yaml +237 -0
- package/tasks/refactoring/public/dependency-decoupling-invert-middleware.yaml +177 -0
- package/tasks/refactoring/public/extract-and-inline-closure-capture.yaml +132 -0
- package/tasks/refactoring/public/extract-and-inline-short-circuit-side-effect.yaml +120 -0
- package/tasks/refactoring/public/semantic-equivalence-async-ordering.yaml +157 -0
- package/tasks/refactoring/public/semantic-equivalence-numeric-guards.yaml +115 -0
- package/tasks/security/public/authz-guard-chain-exposure.yaml +224 -0
- package/tasks/security/public/authz-object-scope-idor.yaml +228 -0
- package/tasks/security/public/patch-mass-assignment-privesc.yaml +226 -0
- package/tasks/security/public/patch-sqli-candidate-fixes.yaml +243 -0
- package/tasks/security/public/supply-lockfile-tamper-trace.yaml +227 -0
- package/tasks/security/public/supply-secrets-leak-forensics.yaml +205 -0
- package/tasks/security/public/taint-export-template-render.yaml +207 -0
- package/tasks/security/public/taint-webhook-outbound-fetch.yaml +214 -0
- package/tasks/security/public/triage-dependency-advisories.yaml +189 -0
- package/tasks/security/public/triage-sast-false-positives.yaml +257 -0
- package/tasks/security/public/vuln-path-sanitizer-escape.yaml +165 -0
- package/tasks/security/public/vuln-token-alg-confusion.yaml +252 -0
- package/tasks/speed/public/speed-api-diff-changelog.yaml +42 -0
- package/tasks/speed/public/speed-callbacks-to-async.yaml +39 -0
- package/tasks/speed/public/speed-class-to-hooks.yaml +47 -0
- package/tasks/speed/public/speed-config-schema-json.yaml +34 -0
- package/tasks/speed/public/speed-csv-report-generator.yaml +38 -0
- package/tasks/speed/public/speed-debounce-hook.yaml +28 -0
- package/tasks/speed/public/speed-log-triage-summary.yaml +35 -0
- package/tasks/speed/public/speed-openapi-fragment.yaml +38 -0
- package/tasks/speed/public/speed-pagination-cursor.yaml +40 -0
- package/tasks/speed/public/speed-rate-limiter-design.yaml +43 -0
- package/tasks/speed/public/speed-rest-client-module.yaml +42 -0
- package/tasks/speed/public/speed-slugify-helper.yaml +30 -0
- package/dist/chunk-4TWPCPRP.cjs.map +0 -1
- package/dist/chunk-7YCJSOK7.cjs.map +0 -1
- package/dist/chunk-CIXITJW6.cjs.map +0 -1
- package/dist/chunk-EQHRUV2I.js.map +0 -1
- package/dist/chunk-JTVNKSMO.js.map +0 -1
- package/dist/chunk-LFKEV2YL.js.map +0 -1
- package/dist/chunk-NJTYVNP4.cjs.map +0 -1
- package/dist/chunk-UECBSKTD.js.map +0 -1
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/client.cjs.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/contracts/index.cjs.map +0 -1
- package/dist/contracts/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tasks.cjs.map +0 -1
- package/dist/tasks.js.map +0 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
id: vuln-token-alg-confusion
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: security
|
|
4
|
+
cluster: vuln-discovery
|
|
5
|
+
difficulty: expert
|
|
6
|
+
title: Latchkey Session-Token Verification Review
|
|
7
|
+
summary: Review a fictional session-token issuance/verification service where one real, reachable token-forgery vulnerability hides among three benign look-alikes (long TTL, Math.random request id, dev-secret fallback) that are each neutralized elsewhere in the artifacts.
|
|
8
|
+
prompt: |
|
|
9
|
+
You are performing a defensive security review of "Latchkey", an internal
|
|
10
|
+
session-token issuance and verification service. Six artifacts describe the
|
|
11
|
+
production deployment: token-issuer, token-verifier, key-loader,
|
|
12
|
+
latchkey-config, keys-endpoint-spec, and integration-note.
|
|
13
|
+
|
|
14
|
+
Exactly one of the behaviors in these artifacts is a real, reachable
|
|
15
|
+
vulnerability; the others are look-alikes that are neutralized somewhere in the
|
|
16
|
+
same artifacts. Answer each deliverable using ONLY the supplied artifacts. Cite
|
|
17
|
+
the artifact id(s) you rely on for every claim. If something is not defined or
|
|
18
|
+
not reachable in the artifacts, say so explicitly. Reason about reachability
|
|
19
|
+
ONLY — do NOT write a forged token, signing code, or any exploit payload.
|
|
20
|
+
|
|
21
|
+
1. Name the single real vulnerability class present in token-verifier, using
|
|
22
|
+
standard terminology, and point to the exact code behavior that constitutes
|
|
23
|
+
it.
|
|
24
|
+
|
|
25
|
+
2. State the precondition / exploitability chain: the specific facts across the
|
|
26
|
+
artifacts that together make it reachable by an external attacker, and what
|
|
27
|
+
capability (not payload) an attacker needs. If any required precondition is
|
|
28
|
+
NOT satisfied by the artifacts, say so.
|
|
29
|
+
|
|
30
|
+
3. A reviewer flags the 24-hour token lifetime (lease of ttlSeconds) as the
|
|
31
|
+
critical issue. Is it? Rule it in or out and cite the mechanism that decides
|
|
32
|
+
it.
|
|
33
|
+
|
|
34
|
+
4. A reviewer flags the Math.random() requestId in token-issuer as a
|
|
35
|
+
weak-randomness vulnerability. Is it? Rule it in or out and cite the
|
|
36
|
+
evidence.
|
|
37
|
+
|
|
38
|
+
5. A reviewer flags the `|| 'dev-secret'` fallback in key-loader as a hardcoded
|
|
39
|
+
secret that is reachable in production. Is it? Rule it in or out and cite the
|
|
40
|
+
mechanism that decides it.
|
|
41
|
+
|
|
42
|
+
6. Give the severity of the one real vulnerability and the minimal control that
|
|
43
|
+
closes it. State the control precisely: what the server must do about the
|
|
44
|
+
algorithm named in the token header.
|
|
45
|
+
artifacts:
|
|
46
|
+
- id: token-issuer
|
|
47
|
+
type: code
|
|
48
|
+
label: Latchkey token issuer (RS256)
|
|
49
|
+
content: |
|
|
50
|
+
// token-issuer.ts — issues Latchkey session tokens. RS256 only.
|
|
51
|
+
import { rsaSign } from './latchkey-crypto';
|
|
52
|
+
import { loadSigningKey } from './key-loader';
|
|
53
|
+
import { config } from './latchkey-config';
|
|
54
|
+
import { randomUuid } from './latchkey-crypto'; // CSPRNG-backed uuid
|
|
55
|
+
|
|
56
|
+
export function issueToken(sub: string): string {
|
|
57
|
+
// requestId is a LOG-CORRELATION tag only. It is never used as a jti,
|
|
58
|
+
// nonce, CSRF token, signature salt, or any secret (see integration-note).
|
|
59
|
+
// Math.random is adequate for correlating log lines; nothing here relies
|
|
60
|
+
// on it being unpredictable.
|
|
61
|
+
const requestId = Math.random().toString(36).slice(2);
|
|
62
|
+
|
|
63
|
+
const header = { alg: 'RS256', typ: 'LST' };
|
|
64
|
+
const now = Math.floor(Date.now() / 1000);
|
|
65
|
+
const claims = {
|
|
66
|
+
sub,
|
|
67
|
+
iss: config.expectedIssuer,
|
|
68
|
+
aud: config.expectedAudience,
|
|
69
|
+
exp: now + config.ttlSeconds, // 24h; every verify still checks revocation
|
|
70
|
+
jti: randomUuid(), // cryptographically random token id (NOT Math.random)
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
log.info('issuing session token', { sub, requestId });
|
|
74
|
+
|
|
75
|
+
const signingInput = `${b64url(header)}.${b64url(claims)}`;
|
|
76
|
+
const sig = rsaSign(signingInput, loadSigningKey().privateKeyPem);
|
|
77
|
+
return `${signingInput}.${sig}`;
|
|
78
|
+
}
|
|
79
|
+
- id: token-verifier
|
|
80
|
+
type: code
|
|
81
|
+
label: Latchkey token verifier
|
|
82
|
+
content: |
|
|
83
|
+
// token-verifier.ts — verifies a compact Latchkey token of the form
|
|
84
|
+
// base64url(header) . base64url(claims) . base64url(signature)
|
|
85
|
+
|
|
86
|
+
import { rsaVerify, hmacVerify } from './latchkey-crypto';
|
|
87
|
+
import { loadVerifyKey } from './key-loader';
|
|
88
|
+
import { isRevoked } from './revocation';
|
|
89
|
+
import { config } from './latchkey-config';
|
|
90
|
+
|
|
91
|
+
export interface Claims { sub: string; iss: string; aud: string; exp: number; jti: string; }
|
|
92
|
+
|
|
93
|
+
export function verifyToken(token: string): Claims {
|
|
94
|
+
const [rawHeader, rawClaims, rawSig] = token.split('.');
|
|
95
|
+
const header = JSON.parse(b64urlDecode(rawHeader)); // attacker-controlled bytes
|
|
96
|
+
const claims = JSON.parse(b64urlDecode(rawClaims)) as Claims;
|
|
97
|
+
const signingInput = `${rawHeader}.${rawClaims}`;
|
|
98
|
+
|
|
99
|
+
// The single verification key this service holds (see key-loader).
|
|
100
|
+
const key = loadVerifyKey();
|
|
101
|
+
|
|
102
|
+
// Choose the verification routine from the algorithm the CALLER named in
|
|
103
|
+
// the token header.
|
|
104
|
+
let signatureOk: boolean;
|
|
105
|
+
switch (header.alg) {
|
|
106
|
+
case 'RS256':
|
|
107
|
+
signatureOk = rsaVerify(signingInput, rawSig, key.publicKeyPem);
|
|
108
|
+
break;
|
|
109
|
+
case 'HS256':
|
|
110
|
+
// Symmetric path for internal callers that cannot do RSA. There is no
|
|
111
|
+
// separate HS256 key configured, so we key the HMAC with the RSA
|
|
112
|
+
// public key material we already hold.
|
|
113
|
+
signatureOk = hmacVerify(signingInput, rawSig, key.publicKeyPem);
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
throw new VerifyError('UNSUPPORTED_ALG');
|
|
117
|
+
}
|
|
118
|
+
if (!signatureOk) throw new VerifyError('BAD_SIGNATURE');
|
|
119
|
+
|
|
120
|
+
// Claim checks are done MANUALLY after the signature check. We deliberately
|
|
121
|
+
// do not pass audience/issuer options into the verify primitives above;
|
|
122
|
+
// these direct comparisons run on every verify.
|
|
123
|
+
if (claims.iss !== config.expectedIssuer) throw new VerifyError('BAD_ISS');
|
|
124
|
+
if (claims.aud !== config.expectedAudience) throw new VerifyError('BAD_AUD');
|
|
125
|
+
if (claims.exp * 1000 <= Date.now()) throw new VerifyError('EXPIRED');
|
|
126
|
+
|
|
127
|
+
// Live revocation: consulted on EVERY verify, so a stolen-but-revoked jti
|
|
128
|
+
// is rejected no matter how far off exp is.
|
|
129
|
+
if (isRevoked(claims.jti)) throw new VerifyError('REVOKED');
|
|
130
|
+
|
|
131
|
+
return claims;
|
|
132
|
+
}
|
|
133
|
+
- id: key-loader
|
|
134
|
+
type: code
|
|
135
|
+
label: Latchkey key material loader
|
|
136
|
+
content: |
|
|
137
|
+
// key-loader.ts — loads Latchkey key material.
|
|
138
|
+
import { config } from './latchkey-config';
|
|
139
|
+
|
|
140
|
+
export interface VerifyKey { publicKeyPem: string; }
|
|
141
|
+
export interface SignKey { privateKeyPem: string; }
|
|
142
|
+
|
|
143
|
+
let cachedVerify: VerifyKey | null = null;
|
|
144
|
+
|
|
145
|
+
// Runs ONCE at boot, before the server binds its port (see integration-note).
|
|
146
|
+
// Outside development the public key MUST be present, so control never reaches
|
|
147
|
+
// the 'dev-secret' fallback below in production — this throws first.
|
|
148
|
+
export function assertKeyMaterial(): void {
|
|
149
|
+
if (config.env !== 'development' && !process.env.LATCHKEY_PUBLIC_KEY) {
|
|
150
|
+
throw new Error('LATCHKEY_PUBLIC_KEY is required outside development');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function loadVerifyKey(): VerifyKey {
|
|
155
|
+
if (cachedVerify) return cachedVerify;
|
|
156
|
+
// In development only, fall back to a fixed string so tests can run without
|
|
157
|
+
// real key material. assertKeyMaterial() has already aborted boot in prod
|
|
158
|
+
// if the real key is missing.
|
|
159
|
+
const pem = process.env.LATCHKEY_PUBLIC_KEY || 'dev-secret';
|
|
160
|
+
cachedVerify = { publicKeyPem: pem };
|
|
161
|
+
return cachedVerify;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function loadSigningKey(): SignKey {
|
|
165
|
+
return { privateKeyPem: requireEnv('LATCHKEY_PRIVATE_KEY') };
|
|
166
|
+
}
|
|
167
|
+
- id: latchkey-config
|
|
168
|
+
type: config
|
|
169
|
+
label: Latchkey runtime configuration (production)
|
|
170
|
+
content: |
|
|
171
|
+
# latchkey.production.yaml — full session-token config, no keys omitted.
|
|
172
|
+
env: production
|
|
173
|
+
expectedIssuer: "https://auth.latchkey.internal"
|
|
174
|
+
expectedAudience: "latchkey-session"
|
|
175
|
+
ttlSeconds: 86400 # 24h session lifetime
|
|
176
|
+
|
|
177
|
+
revocation:
|
|
178
|
+
enabled: true # consulted on EVERY verify (see token-verifier isRevoked)
|
|
179
|
+
store: "latchkey-revset" # shared, synchronous revocation set
|
|
180
|
+
|
|
181
|
+
keys:
|
|
182
|
+
publicKeyEnv: LATCHKEY_PUBLIC_KEY
|
|
183
|
+
privateKeyEnv: LATCHKEY_PRIVATE_KEY
|
|
184
|
+
# The RSA public key is PUBLISHED (see keys-endpoint-spec); the private key
|
|
185
|
+
# is held only by the issuer. No symmetric / shared secret is configured for
|
|
186
|
+
# this service at all — HS256 has no key of its own.
|
|
187
|
+
|
|
188
|
+
# Algorithms the verifier will accept. There is no server-side pin narrower
|
|
189
|
+
# than this list; the verifier selects its routine from the token header.
|
|
190
|
+
allowedAlgs: ["RS256", "HS256"]
|
|
191
|
+
- id: keys-endpoint-spec
|
|
192
|
+
type: spec
|
|
193
|
+
label: Latchkey public key distribution endpoint
|
|
194
|
+
content: |
|
|
195
|
+
Latchkey key distribution — GET /.well-known/latchkey-keys.json
|
|
196
|
+
|
|
197
|
+
Access: PUBLIC, unauthenticated. World-readable by design so any client or
|
|
198
|
+
partner can fetch the key and verify session tokens offline.
|
|
199
|
+
|
|
200
|
+
Response (200):
|
|
201
|
+
{
|
|
202
|
+
"keys": [
|
|
203
|
+
{
|
|
204
|
+
"kid": "lk-2026-03",
|
|
205
|
+
"kty": "RSA",
|
|
206
|
+
"use": "sig",
|
|
207
|
+
"alg": "RS256",
|
|
208
|
+
"pem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkq...AQAB\n-----END PUBLIC KEY-----"
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
The full PEM of the RSA PUBLIC key is served verbatim in the `pem` field. The
|
|
214
|
+
private key is never exposed here or anywhere else.
|
|
215
|
+
|
|
216
|
+
This is the COMPLETE and only key set for the service. There is exactly one
|
|
217
|
+
active verification key — the RSA public key above — and no separate
|
|
218
|
+
HS256 / symmetric key is distributed at this endpoint or anywhere else. Any
|
|
219
|
+
party on the internet can retrieve these exact public key bytes.
|
|
220
|
+
- id: integration-note
|
|
221
|
+
type: note
|
|
222
|
+
label: Latchkey integration notes (authoritative, as-deployed)
|
|
223
|
+
content: |
|
|
224
|
+
Latchkey integration notes — authoritative; reflects production as deployed.
|
|
225
|
+
|
|
226
|
+
- Boot: the server calls assertKeyMaterial() before it binds the port. In
|
|
227
|
+
production (env != development) a missing LATCHKEY_PUBLIC_KEY aborts boot,
|
|
228
|
+
so the 'dev-secret' string in key-loader is never live in production.
|
|
229
|
+
|
|
230
|
+
- Revocation is mandatory and synchronous: verifyToken consults the shared
|
|
231
|
+
revocation set on every call. A token is rejected the instant its jti is
|
|
232
|
+
revoked, so the 24h ttlSeconds is NOT a standing-access window beyond
|
|
233
|
+
revocation. Token lifetime is a product decision; revocation is the security
|
|
234
|
+
control.
|
|
235
|
+
|
|
236
|
+
- requestId (Math.random) is a log-correlation tag ONLY — never a jti, nonce,
|
|
237
|
+
CSRF token, or signature input. The jti is generated by randomUuid(), a
|
|
238
|
+
CSPRNG-backed identifier.
|
|
239
|
+
|
|
240
|
+
- iss and aud are validated inside verifyToken AFTER the signature check, by
|
|
241
|
+
direct comparison against config.expectedIssuer / expectedAudience. We do
|
|
242
|
+
not pass audience/issuer options into the low-level verify primitives; the
|
|
243
|
+
manual comparison is equivalent and runs on every verify.
|
|
244
|
+
|
|
245
|
+
- By design the verifier selects its verification routine from the algorithm
|
|
246
|
+
named in the token header, to let "internal callers that cannot do RSA" use
|
|
247
|
+
HS256.
|
|
248
|
+
|
|
249
|
+
This note, latchkey-config, keys-endpoint-spec, and the three code files are
|
|
250
|
+
the COMPLETE surface for token issuance and verification. There are no other
|
|
251
|
+
verify paths, key sources, or algorithm branches.
|
|
252
|
+
tags: [security, vuln-discovery, jwt, algorithm-confusion, crypto-misuse]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
id: speed-api-diff-changelog
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: retrieval-synthesis
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Write a Changelog Entry From an API Schema Diff
|
|
7
|
+
summary: >-
|
|
8
|
+
Compare two versions of an API response schema, classify each field change as breaking or
|
|
9
|
+
non-breaking, and write a migration-focused changelog entry.
|
|
10
|
+
prompt: |
|
|
11
|
+
Compare [schema-v1] and [schema-v2] for the same endpoint and produce a changelog entry.
|
|
12
|
+
|
|
13
|
+
1. A bulleted list of every field-level change, each tagged (BREAKING) or (non-breaking) with a
|
|
14
|
+
one-clause reason.
|
|
15
|
+
2. A short "Migration" paragraph telling integrators exactly what they must change before upgrading.
|
|
16
|
+
artifacts:
|
|
17
|
+
- id: schema-v1
|
|
18
|
+
type: spec
|
|
19
|
+
label: Response schema v1
|
|
20
|
+
content: |
|
|
21
|
+
GET /users/{id} -> 200 UserV1
|
|
22
|
+
UserV1 = {
|
|
23
|
+
id: string,
|
|
24
|
+
name: string,
|
|
25
|
+
email: string,
|
|
26
|
+
role: "admin" | "member",
|
|
27
|
+
createdAt: string // unix seconds encoded as a numeric string
|
|
28
|
+
}
|
|
29
|
+
- id: schema-v2
|
|
30
|
+
type: spec
|
|
31
|
+
label: Response schema v2
|
|
32
|
+
content: |
|
|
33
|
+
GET /users/{id} -> 200 UserV2
|
|
34
|
+
UserV2 = {
|
|
35
|
+
id: string,
|
|
36
|
+
fullName: string, // renamed from name
|
|
37
|
+
email: string,
|
|
38
|
+
roles: ("admin" | "member" | "billing")[], // was a single role
|
|
39
|
+
createdAt: string, // now ISO-8601 date-time
|
|
40
|
+
avatarUrl: string | null // new, nullable
|
|
41
|
+
}
|
|
42
|
+
tags: [api, changelog, versioning]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
id: speed-callbacks-to-async
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: code-transformation
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Convert a Node Callback Pipeline to Async/Await
|
|
7
|
+
summary: >-
|
|
8
|
+
Rewrite a nested Node.js callback function into an equivalent async/await implementation, preserving
|
|
9
|
+
its result shape and error propagation while parallelizing the two independent reads.
|
|
10
|
+
prompt: |
|
|
11
|
+
Rewrite [callback-code] using async/await and the `fs.promises` API. Preserve the returned object
|
|
12
|
+
shape and error behavior.
|
|
13
|
+
|
|
14
|
+
1. The rewritten async function with the same name and inputs (dropping the trailing callback and
|
|
15
|
+
returning a promise instead).
|
|
16
|
+
2. Any step failing must reject the returned promise with that error.
|
|
17
|
+
3. Read `config.json` first; `a.txt` and `b.txt` are independent, so read them concurrently.
|
|
18
|
+
artifacts:
|
|
19
|
+
- id: callback-code
|
|
20
|
+
type: code
|
|
21
|
+
label: Original callback version
|
|
22
|
+
content: |
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
|
|
25
|
+
function loadBundle(dir, cb) {
|
|
26
|
+
fs.readFile(dir + '/config.json', 'utf8', (e1, cfg) => {
|
|
27
|
+
if (e1) return cb(e1);
|
|
28
|
+
fs.readFile(dir + '/a.txt', 'utf8', (e2, a) => {
|
|
29
|
+
if (e2) return cb(e2);
|
|
30
|
+
fs.readFile(dir + '/b.txt', 'utf8', (e3, b) => {
|
|
31
|
+
if (e3) return cb(e3);
|
|
32
|
+
cb(null, { config: JSON.parse(cfg), a, b });
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { loadBundle };
|
|
39
|
+
tags: [javascript, async, refactor]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
id: speed-class-to-hooks
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: code-transformation
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Migrate a React Class Component to a Function Component With Hooks
|
|
7
|
+
summary: >-
|
|
8
|
+
Convert a small React class component that fetches on mount, polls on an interval, cleans up on
|
|
9
|
+
unmount, and holds two pieces of state into an equivalent function component using hooks.
|
|
10
|
+
prompt: |
|
|
11
|
+
Rewrite [class-component] as a TypeScript function component using hooks. Preserve behavior: fetch on
|
|
12
|
+
mount with abort on unmount, the polling interval, both state fields, and the rendered output.
|
|
13
|
+
|
|
14
|
+
1. The function component using `useState` and `useEffect` with a correct cleanup.
|
|
15
|
+
2. Keep the same props (`serviceId`, `intervalMs`) and the same rendered markup.
|
|
16
|
+
3. On unmount, abort the in-flight fetch and clear the interval.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: class-component
|
|
19
|
+
type: code
|
|
20
|
+
label: Class component
|
|
21
|
+
content: |
|
|
22
|
+
class StatusPanel extends React.Component {
|
|
23
|
+
state = { status: 'loading', lastChecked: null };
|
|
24
|
+
controller = new AbortController();
|
|
25
|
+
timer = undefined;
|
|
26
|
+
|
|
27
|
+
async componentDidMount() {
|
|
28
|
+
const r = await fetch(`/api/status/${this.props.serviceId}`, { signal: this.controller.signal });
|
|
29
|
+
this.setState({ status: (await r.json()).state, lastChecked: Date.now() });
|
|
30
|
+
this.timer = setInterval(() => this.refresh(), this.props.intervalMs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
componentWillUnmount() {
|
|
34
|
+
this.controller.abort();
|
|
35
|
+
clearInterval(this.timer);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async refresh() {
|
|
39
|
+
const r = await fetch(`/api/status/${this.props.serviceId}`);
|
|
40
|
+
this.setState({ status: (await r.json()).state, lastChecked: Date.now() });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render() {
|
|
44
|
+
return <div>{this.state.status} @ {String(this.state.lastChecked)}</div>;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
tags: [react, hooks, migration]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
id: speed-config-schema-json
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: structured-output
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Author a JSON Schema for a Service Config File
|
|
7
|
+
summary: >-
|
|
8
|
+
Write a strict draft 2020-12 JSON Schema validating a service configuration object, enforcing types,
|
|
9
|
+
enums, numeric ranges, a conditional requirement, and no unexpected properties.
|
|
10
|
+
prompt: |
|
|
11
|
+
Produce a single JSON Schema (draft 2020-12) that validates the config described in [config-spec].
|
|
12
|
+
Emit only the JSON.
|
|
13
|
+
|
|
14
|
+
1. Types, `enum`s, defaults, and numeric bounds for every field.
|
|
15
|
+
2. `additionalProperties: false` at each fixed object level and a correct top-level `required` list.
|
|
16
|
+
3. A conditional: when `tls.enabled` is true, `tls.certPath` and `tls.keyPath` are required.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: config-spec
|
|
19
|
+
type: spec
|
|
20
|
+
label: Config shape
|
|
21
|
+
content: |
|
|
22
|
+
serviceName : string, required, 1..64 characters
|
|
23
|
+
port : integer, required, 1..65535
|
|
24
|
+
logLevel : one of "debug" | "info" | "warn" | "error", default "info"
|
|
25
|
+
replicas : integer, 1..50, default 1
|
|
26
|
+
tls : object, required
|
|
27
|
+
tls.enabled : boolean, required
|
|
28
|
+
tls.certPath : string
|
|
29
|
+
tls.keyPath : string
|
|
30
|
+
featureFlags: optional object mapping arbitrary string keys to boolean values
|
|
31
|
+
|
|
32
|
+
No properties beyond those listed are permitted at any level, except that featureFlags may hold
|
|
33
|
+
arbitrary string -> boolean entries.
|
|
34
|
+
tags: [json-schema, validation, config]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
id: speed-csv-report-generator
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: long-generation
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Build a CSV Sales-Summary Generator From In-Memory Rows
|
|
7
|
+
summary: >-
|
|
8
|
+
Write a complete TypeScript module that aggregates order rows into a per-region monthly summary and
|
|
9
|
+
serializes it to RFC 4180 CSV, including proper quoting and a totals row.
|
|
10
|
+
prompt: |
|
|
11
|
+
Implement `salesSummaryCsv(rows: OrderRow[]): string` in TypeScript per [summary-spec] and
|
|
12
|
+
[csv-rules]. Emit the whole module, including any helpers.
|
|
13
|
+
|
|
14
|
+
1. The `OrderRow` interface and the `salesSummaryCsv` function.
|
|
15
|
+
2. Correct RFC 4180 quoting for fields containing commas, quotes, or newlines.
|
|
16
|
+
3. A trailing `TOTAL` row summing net revenue across all regions and months.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: summary-spec
|
|
19
|
+
type: spec
|
|
20
|
+
label: Aggregation
|
|
21
|
+
content: |
|
|
22
|
+
OrderRow = { region: string; isoDate: string; revenue: number; refunded: boolean }
|
|
23
|
+
- group by (region, YYYY-MM derived from isoDate)
|
|
24
|
+
- net revenue per group = sum(revenue where refunded === false) - sum(revenue where refunded === true)
|
|
25
|
+
- output columns, in order: Region,Month,Orders,NetRevenue
|
|
26
|
+
- Orders counts every row in the group regardless of refunded
|
|
27
|
+
- sort output rows by Region ascending, then Month ascending
|
|
28
|
+
- NetRevenue is formatted to exactly two decimals
|
|
29
|
+
- id: csv-rules
|
|
30
|
+
type: spec
|
|
31
|
+
label: CSV format
|
|
32
|
+
content: |
|
|
33
|
+
- RFC 4180: any field containing a comma, double-quote, CR, or LF is wrapped in double quotes
|
|
34
|
+
- an embedded double-quote is escaped by doubling it
|
|
35
|
+
- a header row comes first; rows are joined with CRLF
|
|
36
|
+
- the final TOTAL row uses "TOTAL" in Region, an empty Month, the total order count, and the
|
|
37
|
+
summed NetRevenue across every group
|
|
38
|
+
tags: [typescript, csv, reporting]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: speed-debounce-hook
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: short-completion
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Write a Typed useDebouncedValue React Hook
|
|
7
|
+
summary: >-
|
|
8
|
+
Implement a small, correct React hook that returns a debounced copy of a value, honoring the
|
|
9
|
+
behavioral contract for cleanup, delay changes, and the initial render.
|
|
10
|
+
prompt: |
|
|
11
|
+
Implement `useDebouncedValue` in TypeScript for React 18 exactly per [hook-contract]. Return only the
|
|
12
|
+
hook implementation and its import line.
|
|
13
|
+
|
|
14
|
+
1. The `useDebouncedValue<T>(value: T, delayMs: number): T` implementation.
|
|
15
|
+
2. A one-sentence note stating what happens to a pending update when `value` changes again before
|
|
16
|
+
`delayMs` elapses.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: hook-contract
|
|
19
|
+
type: spec
|
|
20
|
+
label: Hook behavior
|
|
21
|
+
content: |
|
|
22
|
+
useDebouncedValue<T>(value, delayMs)
|
|
23
|
+
- returns the initial value immediately on first render (no delay)
|
|
24
|
+
- after value changes, returns the new value only once delayMs has elapsed with no further change
|
|
25
|
+
- a change during the wait window cancels the previous pending update and restarts the timer
|
|
26
|
+
- clears its timer on unmount
|
|
27
|
+
- re-reads delayMs on every change, so a changed delay applies to the next debounce window
|
|
28
|
+
tags: [react, hooks, typescript]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: speed-log-triage-summary
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: retrieval-synthesis
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Synthesize an Incident Summary From Logs and a Deploy Record
|
|
7
|
+
summary: >-
|
|
8
|
+
Read a short application log excerpt and a deploy record, correlate them, and produce a concise
|
|
9
|
+
incident summary plus the single configuration change that restores prior behavior.
|
|
10
|
+
prompt: |
|
|
11
|
+
Using [app-log] and [deploy-record], write a short incident summary and a one-line configuration fix.
|
|
12
|
+
|
|
13
|
+
1. A 3-4 sentence summary: what failed, the first timestamp it appeared, the likely trigger
|
|
14
|
+
(correlated with [deploy-record]), and the user-visible impact.
|
|
15
|
+
2. The single environment-variable change (name and value) that would restore the prior behavior,
|
|
16
|
+
citing the log line that identifies it.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: app-log
|
|
19
|
+
type: log
|
|
20
|
+
label: Application log excerpt
|
|
21
|
+
content: |
|
|
22
|
+
2026-05-02T14:02:11Z INFO boot: worker started, pool size 20
|
|
23
|
+
2026-05-02T14:03:40Z INFO deploy: version 4.7.0 live
|
|
24
|
+
2026-05-02T14:03:41Z WARN db: DB_POOL_MAX=5 (was 20) applied from env
|
|
25
|
+
2026-05-02T14:04:02Z ERROR db: pool exhausted, 15 requests queued > 3000ms
|
|
26
|
+
2026-05-02T14:04:03Z ERROR http: 503 returned to /checkout (db timeout)
|
|
27
|
+
2026-05-02T14:06:00Z ERROR http: 503 rate 34% on /checkout
|
|
28
|
+
- id: deploy-record
|
|
29
|
+
type: table
|
|
30
|
+
label: Deploy record
|
|
31
|
+
content: |
|
|
32
|
+
version | time | notes
|
|
33
|
+
4.6.3 | 2026-04-28T09:00:00Z | baseline, DB_POOL_MAX=20
|
|
34
|
+
4.7.0 | 2026-05-02T14:03:40Z | env refactor: consolidated pool settings
|
|
35
|
+
tags: [incident, logs, synthesis]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
id: speed-openapi-fragment
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: structured-output
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Emit an OpenAPI 3.1 Path Fragment for a Documented Endpoint
|
|
7
|
+
summary: >-
|
|
8
|
+
Produce a valid OpenAPI 3.1 YAML fragment describing one endpoint, its path parameter, request body,
|
|
9
|
+
and two response codes, matching the written contract exactly.
|
|
10
|
+
prompt: |
|
|
11
|
+
Given [endpoint-contract], output a single OpenAPI 3.1 YAML fragment containing the `paths` entry for
|
|
12
|
+
`POST /projects/{projectId}/tasks` and the `components/schemas` it references. Emit only YAML.
|
|
13
|
+
|
|
14
|
+
1. The `paths` object for the endpoint, including the path parameter, request body, and the 201 and
|
|
15
|
+
422 responses.
|
|
16
|
+
2. `components/schemas` entries `TaskInput` and `Task` matching the field list, with required fields,
|
|
17
|
+
enums, and formats.
|
|
18
|
+
artifacts:
|
|
19
|
+
- id: endpoint-contract
|
|
20
|
+
type: spec
|
|
21
|
+
label: Endpoint contract
|
|
22
|
+
content: |
|
|
23
|
+
POST /projects/{projectId}/tasks
|
|
24
|
+
- path param projectId: string, format uuid, required
|
|
25
|
+
- request body (application/json), required: TaskInput
|
|
26
|
+
TaskInput = {
|
|
27
|
+
title: string, 1..200 chars, required
|
|
28
|
+
assigneeId?: string (uuid)
|
|
29
|
+
priority: one of "low" | "med" | "high", default "med"
|
|
30
|
+
}
|
|
31
|
+
- 201 response (application/json): Task
|
|
32
|
+
Task = {
|
|
33
|
+
id: string uuid; projectId: string uuid; title: string;
|
|
34
|
+
assigneeId: string | null; priority: string; createdAt: string date-time
|
|
35
|
+
}
|
|
36
|
+
- 422 response (application/json): { message: string; field?: string }
|
|
37
|
+
- security: bearerAuth
|
|
38
|
+
tags: [openapi, yaml, api-design]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: speed-pagination-cursor
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: stepwise-reasoning
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Design a Stable Keyset Cursor and Encode Three Pages
|
|
7
|
+
summary: >-
|
|
8
|
+
Specify and implement opaque keyset-pagination cursors for a sorted feed, then show the exact page
|
|
9
|
+
predicate and cursor values while paging through a small dataset.
|
|
10
|
+
prompt: |
|
|
11
|
+
Per [feed-spec], implement cursor encode/decode and the next-page predicate, then trace paging over
|
|
12
|
+
[feed-rows] with page size 2.
|
|
13
|
+
|
|
14
|
+
1. `encodeCursor` / `decodeCursor` (base64url of the JSON keyset) plus the `WHERE` and `ORDER BY`
|
|
15
|
+
clause for the next page given a decoded cursor.
|
|
16
|
+
2. Walk [feed-rows] in pages of 2: for each page list the returned ids and the `nextCursor`
|
|
17
|
+
(its decoded form is fine), stopping when the feed is exhausted.
|
|
18
|
+
artifacts:
|
|
19
|
+
- id: feed-spec
|
|
20
|
+
type: spec
|
|
21
|
+
label: Feed ordering and cursor
|
|
22
|
+
content: |
|
|
23
|
+
Rows are ordered by (score DESC, id ASC). A cursor is an opaque base64url token encoding the last
|
|
24
|
+
returned row's { score, id }. The next page selects rows strictly after the cursor in that order:
|
|
25
|
+
WHERE (score < cur.score) OR (score = cur.score AND id > cur.id)
|
|
26
|
+
ORDER BY score DESC, id ASC
|
|
27
|
+
LIMIT pageSize
|
|
28
|
+
The first page uses no cursor. nextCursor is null when fewer than pageSize rows are returned.
|
|
29
|
+
- id: feed-rows
|
|
30
|
+
type: table
|
|
31
|
+
label: Rows
|
|
32
|
+
content: |
|
|
33
|
+
id | score
|
|
34
|
+
a | 90
|
|
35
|
+
b | 90
|
|
36
|
+
c | 80
|
|
37
|
+
d | 80
|
|
38
|
+
e | 80
|
|
39
|
+
f | 70
|
|
40
|
+
tags: [pagination, sql, api-design]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
id: speed-rate-limiter-design
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
category: speed
|
|
4
|
+
cluster: stepwise-reasoning
|
|
5
|
+
difficulty: hard
|
|
6
|
+
title: Implement a Token-Bucket Rate Limiter and Trace Its Behavior
|
|
7
|
+
summary: >-
|
|
8
|
+
Write a token-bucket rate limiter to a precise spec, then walk a given request timeline step by step
|
|
9
|
+
to state which requests are allowed or rejected and the tokens remaining.
|
|
10
|
+
prompt: |
|
|
11
|
+
Using [bucket-spec], implement the limiter and then apply it to [request-trace].
|
|
12
|
+
|
|
13
|
+
1. Implement `class TokenBucket` in TypeScript with `tryRemove(now: number, cost = 1): boolean`.
|
|
14
|
+
2. For the eight timestamped requests in [request-trace], produce a table with columns:
|
|
15
|
+
request #, time, tokens-before, allowed?, tokens-after. Show the refill arithmetic for at least
|
|
16
|
+
two of the steps.
|
|
17
|
+
artifacts:
|
|
18
|
+
- id: bucket-spec
|
|
19
|
+
type: spec
|
|
20
|
+
label: Token bucket
|
|
21
|
+
content: |
|
|
22
|
+
- capacity = 5 tokens; refillRate = 1 token per 1000 ms
|
|
23
|
+
- the bucket starts full (5 tokens) at t = 0, with lastRefill = 0
|
|
24
|
+
- on each call, refill first:
|
|
25
|
+
stored = min(capacity, stored + (now - lastRefill) / 1000 * refillRate)
|
|
26
|
+
lastRefill = now
|
|
27
|
+
- tokens are fractional internally
|
|
28
|
+
- tryRemove(now, cost): refill, then if stored >= cost subtract cost and return true,
|
|
29
|
+
otherwise leave stored unchanged and return false
|
|
30
|
+
- id: request-trace
|
|
31
|
+
type: table
|
|
32
|
+
label: Request timeline
|
|
33
|
+
content: |
|
|
34
|
+
request # | time(ms) | cost
|
|
35
|
+
1 | 0 | 3
|
|
36
|
+
2 | 200 | 2
|
|
37
|
+
3 | 500 | 1
|
|
38
|
+
4 | 900 | 1
|
|
39
|
+
5 | 1500 | 2
|
|
40
|
+
6 | 1600 | 1
|
|
41
|
+
7 | 4000 | 5
|
|
42
|
+
8 | 4200 | 1
|
|
43
|
+
tags: [algorithms, rate-limiting, typescript]
|