chore: import upstream snapshot with attribution
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:24:08 +08:00
commit 0d3cb498a3
5438 changed files with 1316560 additions and 0 deletions
@@ -0,0 +1,17 @@
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { extractModuleSpecifiers } from '../../scripts/architectureUtils';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
describe('evaluator store boundary', () => {
it('keeps the evaluator independent from the concrete Eval model', () => {
const evaluatorPath = 'src/evaluator.ts';
const source = readFileSync(path.join(repoRoot, evaluatorPath), 'utf8');
expect(extractModuleSpecifiers(source, evaluatorPath)).not.toContain('./models/eval');
});
});
@@ -0,0 +1,149 @@
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
// Entry-point files that resolve a provider ID to a factory. Per-provider
// implementation files (e.g. src/providers/promptfoo.ts) may still import
// redteam utilities for unrelated reasons — the invariant this test protects
// is specifically that the *dispatch* path stays free of static redteam
// imports, so it is enumerated rather than globbed.
const providerLoadingFiles = [
'src/providers/index.ts',
'src/providers/registry.ts',
'src/providers/registryTypes.ts',
];
// Match both `import … from '…'` / `export … from '…'` AND bare side-effect
// imports `import '…';`. A single combined regex with an optional `from`
// clause backtracks incorrectly across multi-line imports (the lazy body
// happily hops to the next `from` on the next line), so the two shapes are
// matched with separate patterns and merged.
//
// Type-only imports (`import type`, `export type`) are tagged so the caller
// can skip them in the violation check — TypeScript erases them at build
// time, so they cannot cause runtime eager loading and therefore do not
// defeat the lazy-loading boundary this test exists to enforce.
interface ImportSpecifier {
specifier: string;
typeOnly: boolean;
}
function getStaticImportSpecifiers(source: string): ImportSpecifier[] {
const fromPattern =
/(?:^|\n)\s*(?:import|export)\s+(type\s+)?[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
// Bare side-effect imports like `import '../redteam/side-effects';` — they
// have no `from` clause, so the `fromPattern` never sees them. They are
// still load-bearing at module eval time and would defeat the boundary this
// test exists to enforce. Side-effect imports are never type-only.
const sideEffectPattern = /(?:^|\n)\s*import\s+['"]([^'"]+)['"]\s*;?/g;
const specifiers: ImportSpecifier[] = [];
for (const match of source.matchAll(fromPattern)) {
specifiers.push({ specifier: match[2], typeOnly: Boolean(match[1]) });
}
for (const match of source.matchAll(sideEffectPattern)) {
specifiers.push({ specifier: match[1], typeOnly: false });
}
return specifiers;
}
function isRedteamSpecifier(specifier: string): boolean {
return specifier.includes('/redteam') || specifier.startsWith('../redteam');
}
describe('provider/redteam module boundary', () => {
it.each(providerLoadingFiles)('%s exists and has static imports', (relativePath) => {
const absolute = path.join(repoRoot, relativePath);
expect(existsSync(absolute), `boundary file missing: ${relativePath}`).toBe(true);
const specifiers = getStaticImportSpecifiers(readFileSync(absolute, 'utf8'));
// A provider-loading file with zero specifiers signals either an
// accidentally-emptied file or a regex that stopped matching — both of
// which would silently pass the redteam-import check below.
expect(specifiers.length, `${relativePath} produced no import specifiers`).toBeGreaterThan(0);
});
it('keeps provider loading modules free of runtime redteam imports', () => {
// Only runtime imports count as violations. Type-only imports
// (`import type { X } from '../redteam/...'`) are erased at build time
// and cannot cause eager module loading, so they are intentionally
// allowed.
const violations = providerLoadingFiles.flatMap((relativePath) => {
const source = readFileSync(path.join(repoRoot, relativePath), 'utf8');
return getStaticImportSpecifiers(source)
.filter((entry) => !entry.typeOnly && isRedteamSpecifier(entry.specifier))
.map((entry) => `${relativePath} imports ${entry.specifier}`);
});
expect(violations).toEqual([]);
});
it('violation filter catches a synthetic runtime redteam import', () => {
// Positive control: prove the filter + regex pipeline actually fires on
// a known violator. A future refactor that accidentally disabled the
// violation check (e.g. by filtering everything out) would be caught
// here instead of silently passing the production-file scan above.
const fixture = `
import { SomeClass } from '../redteam/runtime-violator';
`;
const violations = getStaticImportSpecifiers(fixture)
.filter((entry) => !entry.typeOnly && isRedteamSpecifier(entry.specifier))
.map((entry) => entry.specifier);
expect(violations).toEqual(['../redteam/runtime-violator']);
});
it('violation filter ignores type-only redteam imports', () => {
const fixture = `
import type { RedteamFoo } from '../redteam/types';
export type { RedteamBar } from '../redteam/other-types';
`;
const violations = getStaticImportSpecifiers(fixture)
.filter((entry) => !entry.typeOnly && isRedteamSpecifier(entry.specifier))
.map((entry) => entry.specifier);
expect(violations).toEqual([]);
});
it('getStaticImportSpecifiers catches bare side-effect imports', () => {
// Regression guard: the previous regex required a `from` clause and would
// silently miss `import '../redteam/foo';` side-effect imports. This unit
// test pins the behaviour of the matcher itself so future edits cannot
// accidentally reintroduce that gap without a failing test.
const fixture = `
import normal from 'normal';
import { named } from 'named';
import 'side-effect-only';
import '../redteam/side-effect';
export { foo } from 'reexport';
`;
const specifiers = getStaticImportSpecifiers(fixture).map((entry) => entry.specifier);
// Order comes from running the two-regex passes sequentially (from-style
// first, then side-effect imports). Sort before asserting so the test is
// order-independent.
expect([...specifiers].sort()).toEqual(
['../redteam/side-effect', 'named', 'normal', 'reexport', 'side-effect-only'].sort(),
);
});
it('getStaticImportSpecifiers tags type-only imports as typeOnly', () => {
// Pin the type-only tagging so a future edit that breaks the
// `(type\s+)?` capture group fails this test instead of silently
// reclassifying runtime imports as type-only (which would let them
// bypass the violation check above).
const fixture = `
import type { X } from 'x';
export type { Y } from 'y';
import { Z } from 'z';
`;
const byName = new Map(
getStaticImportSpecifiers(fixture).map((entry) => [entry.specifier, entry.typeOnly]),
);
expect(byName.get('x')).toBe(true);
expect(byName.get('y')).toBe(true);
expect(byName.get('z')).toBe(false);
});
});