mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
Tasks 1.1, 5.1, 5.2, 5.4 and the data half of 5.3. - mechanisms.toml: closed vocabulary v1 (20 ids) every expected row cites. - vibe-py: 35 Real-Vuln-Benchmark pairs (Apache-2.0), human provenance only because the LLM-generated repositories carry no license (escalated). - vfc-js: 17 Node fix-commit pairs harvested upstream (MIT/Apache-2.0), SecBench.js itself is not vendored (no license); sinks derived on 13/17. - agent-vfc: 29 agent-trailer fixes as catalog-candidates.toml plus REVIEW.md; the loaded catalog stays empty until a human reviews them. - datasets.toml and README document every slice and its review status. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
138 lines
3.7 KiB
TypeScript
138 lines
3.7 KiB
TypeScript
// Provenance: G4brym/workers-research (vuln).
|
|
// repo: G4brym/workers-research
|
|
// commit: eab923ad3099c01b002fbd6be7cf994614bbdee4
|
|
// parent: eab923ad3099c01b002fbd6be7cf994614bbdee4
|
|
// commit_url: https://github.com/G4brym/workers-research/commit/209444eb61cc564c2d6b43267256f85fe3e5e30e
|
|
// cve:
|
|
// license: MIT
|
|
// function: formatDuration
|
|
// relpath: src/utils.ts
|
|
// provenance: agent
|
|
// mechanism: source_reaches_sink
|
|
|
|
import {
|
|
createGoogleGenerativeAI,
|
|
type GoogleGenerativeAIProvider,
|
|
type GoogleGenerativeAIProviderSettings,
|
|
} from "@ai-sdk/google";
|
|
import type { Env } from "./bindings";
|
|
|
|
function getGoogleProvider(env: Env): GoogleGenerativeAIProvider {
|
|
const args: GoogleGenerativeAIProviderSettings = {
|
|
apiKey: env.GOOGLE_API_KEY,
|
|
};
|
|
|
|
if (env.AI_GATEWAY_ACCOUNT_ID && env.AI_GATEWAY_NAME) {
|
|
args.baseURL = `https://gateway.ai.cloudflare.com/v1/${env.AI_GATEWAY_ACCOUNT_ID}/${env.AI_GATEWAY_NAME}/google-ai-studio/v1beta`;
|
|
|
|
if (env.AI_GATEWAY_API_KEY) {
|
|
args.headers = {
|
|
"cf-aig-authorization": `Bearer ${env.AI_GATEWAY_API_KEY}`,
|
|
};
|
|
}
|
|
}
|
|
|
|
return createGoogleGenerativeAI(args);
|
|
}
|
|
|
|
export function getModel(env: Env) {
|
|
const google = getGoogleProvider(env);
|
|
|
|
return google("gemini-2.0-flash-exp");
|
|
}
|
|
|
|
export function getFallbackModel(env: Env) {
|
|
const google = getGoogleProvider(env);
|
|
return google("gemini-2.0-flash");
|
|
}
|
|
|
|
export function getModelThinking(env: Env) {
|
|
const google = getGoogleProvider(env);
|
|
|
|
return google("gemini-2.0-flash-exp");
|
|
}
|
|
|
|
export function timeAgo(date: Date): string {
|
|
const now = new Date();
|
|
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
|
|
|
const intervals: [number, string][] = [
|
|
[60, "minute"],
|
|
[60, "hour"],
|
|
[24, "day"],
|
|
[7, "week"],
|
|
[4.35, "month"],
|
|
[12, "year"],
|
|
];
|
|
|
|
let count = seconds;
|
|
let unit = "second";
|
|
|
|
for (const [interval, name] of intervals) {
|
|
if (count < interval) break;
|
|
count /= interval;
|
|
unit = name;
|
|
}
|
|
|
|
count = Math.floor(count);
|
|
return `${count} ${unit}${count !== 1 ? "s" : ""} ago`;
|
|
}
|
|
|
|
export async function sleep(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* Normalize a domain input for excluded domain matching.
|
|
* Handles common user input formats:
|
|
* - Full URLs: "https://reddit.com/foo" → "reddit.com"
|
|
* - www prefix: "www.reddit.com" → "reddit.com"
|
|
* - Mixed: "https://www.reddit.com" → "reddit.com"
|
|
* - Plain domains are returned as-is (lowercased, trimmed)
|
|
*/
|
|
export function normalizeDomain(input: string): string {
|
|
let domain = input.trim().toLowerCase();
|
|
if (domain.length === 0) return "";
|
|
|
|
// If it looks like a URL (has ://), extract the hostname
|
|
if (domain.includes("://")) {
|
|
try {
|
|
domain = new URL(domain).hostname;
|
|
} catch {
|
|
// If URL parsing fails, strip the protocol manually
|
|
domain = domain.replace(/^[a-z]+:\/\//, "").split("/")[0];
|
|
}
|
|
} else {
|
|
// Strip any trailing path from plain domain input
|
|
domain = domain.split("/")[0];
|
|
}
|
|
|
|
// Strip www. prefix for consistent matching
|
|
domain = domain.replace(/^www\./, "");
|
|
|
|
return domain;
|
|
}
|
|
|
|
export function formatDuration(ms: number): string {
|
|
// Handle negative or zero values
|
|
if (ms <= 0) {
|
|
return "0.0 seconds";
|
|
}
|
|
|
|
const seconds = ms / 1000;
|
|
const minutes = seconds / 60;
|
|
const hours = minutes / 60;
|
|
const days = hours / 24;
|
|
|
|
// Determine the appropriate unit and format with one decimal place
|
|
if (days >= 1) {
|
|
return `${days.toFixed(1)} day${days !== 1 ? "s" : ""}`;
|
|
} else if (hours >= 1) {
|
|
return `${hours.toFixed(1)} hour${hours !== 1 ? "s" : ""}`;
|
|
} else if (minutes >= 1) {
|
|
return `${minutes.toFixed(1)} minute${minutes !== 1 ? "s" : ""}`;
|
|
} else {
|
|
return `${seconds.toFixed(1)} second${seconds !== 1 ? "s" : ""}`;
|
|
}
|
|
}
|