mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:40:43 +00:00
test: share oauth fuzz utilities
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
normalizeAuthIdentityToken,
|
||||
shouldMirrorRefreshedOAuthCredential,
|
||||
} from "./oauth-identity.js";
|
||||
import { makeSeededRandom, maybe, randomAsciiString as randomString } from "./oauth-test-utils.js";
|
||||
import type { AuthProfileCredential } from "./types.js";
|
||||
|
||||
// Direct unit + fuzz tests for the cross-agent credential-mirroring identity
|
||||
@@ -177,30 +178,6 @@ describe("isSameOAuthIdentity", () => {
|
||||
// Fuzz tests. Seeded Mulberry32 so the run is reproducible.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function makeSeededRandom(seed: number): () => number {
|
||||
let t = seed >>> 0;
|
||||
return () => {
|
||||
t = (t + 0x6d2b79f5) >>> 0;
|
||||
let r = t;
|
||||
r = Math.imul(r ^ (r >>> 15), r | 1);
|
||||
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
|
||||
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
function randomString(rng: () => number, maxLen: number): string {
|
||||
const len = Math.floor(rng() * maxLen);
|
||||
const chars: string[] = [];
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
chars.push(String.fromCodePoint(32 + Math.floor(rng() * 95))); // printable ASCII
|
||||
}
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
function maybe<T>(rng: () => number, value: T): T | undefined {
|
||||
return rng() < 0.5 ? value : undefined;
|
||||
}
|
||||
|
||||
describe("isSafeToCopyOAuthIdentity (unified copy gate, used for mirror and adopt)", () => {
|
||||
describe("positive matches", () => {
|
||||
it("accepts matching accountIds", () => {
|
||||
@@ -479,30 +456,6 @@ describe("shouldMirrorRefreshedOAuthCredential", () => {
|
||||
});
|
||||
|
||||
describe("isSafeToCopyOAuthIdentity fuzz", () => {
|
||||
function makeSeededRandom(seed: number): () => number {
|
||||
let t = seed >>> 0;
|
||||
return () => {
|
||||
t = (t + 0x6d2b79f5) >>> 0;
|
||||
let r = t;
|
||||
r = Math.imul(r ^ (r >>> 15), r | 1);
|
||||
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
|
||||
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
function randomString(rng: () => number, maxLen: number): string {
|
||||
const len = Math.floor(rng() * maxLen);
|
||||
const chars: string[] = [];
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
chars.push(String.fromCodePoint(32 + Math.floor(rng() * 95)));
|
||||
}
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
function maybe<T>(rng: () => number, value: T): T | undefined {
|
||||
return rng() < 0.5 ? value : undefined;
|
||||
}
|
||||
|
||||
it("is reflexive: share(a, a) is always true", () => {
|
||||
const rng = makeSeededRandom(0x0172_0417);
|
||||
for (let i = 0; i < 1000; i += 1) {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
makeSeededRandom,
|
||||
randomAsciiString as randomJunk,
|
||||
randomlyCased,
|
||||
} from "./oauth-test-utils.js";
|
||||
import { isRefreshTokenReusedError } from "./oauth.js";
|
||||
|
||||
// Direct tests for the refresh_token_reused classifier. This is the gate that
|
||||
@@ -94,33 +99,6 @@ describe("isRefreshTokenReusedError", () => {
|
||||
});
|
||||
|
||||
describe("fuzz: random noisy messages", () => {
|
||||
function makeSeededRandom(seed: number): () => number {
|
||||
let t = seed >>> 0;
|
||||
return () => {
|
||||
t = (t + 0x6d2b79f5) >>> 0;
|
||||
let r = t;
|
||||
r = Math.imul(r ^ (r >>> 15), r | 1);
|
||||
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
|
||||
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
function randomJunk(rng: () => number, maxLen: number): string {
|
||||
const len = Math.floor(rng() * maxLen);
|
||||
const chars: string[] = [];
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
chars.push(String.fromCodePoint(32 + Math.floor(rng() * 95)));
|
||||
}
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
function randomlyCased(s: string, rng: () => number): string {
|
||||
return s
|
||||
.split("")
|
||||
.map((c) => (rng() < 0.5 ? c.toUpperCase() : c.toLowerCase()))
|
||||
.join("");
|
||||
}
|
||||
|
||||
it("always detects the marker when embedded at random positions with noise", () => {
|
||||
const rng = makeSeededRandom(0xabad1dea);
|
||||
const markers = [
|
||||
|
||||
@@ -52,3 +52,34 @@ export function createExpiredOauthStore(params: {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function makeSeededRandom(seed: number): () => number {
|
||||
let state = seed >>> 0;
|
||||
return () => {
|
||||
state = (state + 0x6d2b79f5) >>> 0;
|
||||
let value = state;
|
||||
value = Math.imul(value ^ (value >>> 15), value | 1);
|
||||
value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
|
||||
return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
export function randomAsciiString(rng: () => number, maxLen: number): string {
|
||||
const len = Math.floor(rng() * maxLen);
|
||||
const chars: string[] = [];
|
||||
for (let index = 0; index < len; index += 1) {
|
||||
chars.push(String.fromCodePoint(32 + Math.floor(rng() * 95)));
|
||||
}
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
export function maybe<T>(rng: () => number, value: T): T | undefined {
|
||||
return rng() < 0.5 ? value : undefined;
|
||||
}
|
||||
|
||||
export function randomlyCased(value: string, rng: () => number): string {
|
||||
return value
|
||||
.split("")
|
||||
.map((char) => (rng() < 0.5 ? char.toUpperCase() : char.toLowerCase()))
|
||||
.join("");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user