Add Authority Advisory AI and API Lifecycle Configuration

- Introduced AuthorityAdvisoryAiOptions and related classes for managing advisory AI configurations, including remote inference options and tenant-specific settings.
- Added AuthorityApiLifecycleOptions to control API lifecycle settings, including legacy OAuth endpoint configurations.
- Implemented validation and normalization methods for both advisory AI and API lifecycle options to ensure proper configuration.
- Created AuthorityNotificationsOptions and its related classes for managing notification settings, including ack tokens, webhooks, and escalation options.
- Developed IssuerDirectoryClient and related models for interacting with the issuer directory service, including caching mechanisms and HTTP client configurations.
- Added support for dependency injection through ServiceCollectionExtensions for the Issuer Directory Client.
- Updated project file to include necessary package references for the new Issuer Directory Client library.
This commit is contained in:
master
2025-11-02 13:40:38 +02:00
parent 66cb6c4b8a
commit f98cea3bcf
516 changed files with 68157 additions and 24754 deletions

View File

@@ -0,0 +1,145 @@
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { spawnSync } from 'node:child_process';
import Ajv2020 from 'ajv/dist/2020.js';
import addFormats from 'ajv-formats';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const repoRoot = join(__dirname, '..');
const moduleRoot = join(repoRoot, 'src', 'Attestor', 'StellaOps.Attestor.Types');
const schemasDir = join(moduleRoot, 'schemas');
const fixturesDir = join(moduleRoot, 'fixtures', 'v1');
const tsDir = join(moduleRoot, 'generated', 'ts');
const goDir = join(moduleRoot, 'generated', 'go');
const schemaFiles = [
{ schema: 'stellaops-build-provenance.v1.schema.json', sample: 'build-provenance.sample.json' },
{ schema: 'stellaops-sbom-attestation.v1.schema.json', sample: 'sbom-attestation.sample.json' },
{ schema: 'stellaops-scan-results.v1.schema.json', sample: 'scan-results.sample.json' },
{ schema: 'stellaops-vex-attestation.v1.schema.json', sample: 'vex-attestation.sample.json' },
{ schema: 'stellaops-policy-evaluation.v1.schema.json', sample: 'policy-evaluation.sample.json' },
{ schema: 'stellaops-risk-profile.v1.schema.json', sample: 'risk-profile-evidence.sample.json' },
{ schema: 'stellaops-custom-evidence.v1.schema.json', sample: 'custom-evidence.sample.json' }
];
const commonSchemaPath = join(schemasDir, 'attestation-common.v1.schema.json');
const ajv = new Ajv2020({ strict: false, allErrors: true });
addFormats(ajv);
const commonSchema = JSON.parse(readFileSync(commonSchemaPath, 'utf8'));
const commonId = commonSchema.$id || 'https://schemas.stella-ops.org/attestations/common/v1';
ajv.addSchema(commonSchema, commonId);
let failed = false;
function stableStringify(value) {
if (Array.isArray(value)) {
return '[' + value.map(stableStringify).join(',') + ']';
}
if (value && typeof value === 'object') {
const entries = Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`);
return '{' + entries.join(',') + '}';
}
return JSON.stringify(value);
}
function runCommand(command, args, options) {
const result = spawnSync(command, args, { stdio: 'inherit', ...options });
if (result.error) {
if (result.error.code === 'ENOENT') {
throw new Error(`Command not found: ${command}`);
}
throw result.error;
}
if (result.status !== 0) {
throw new Error(`Command failed: ${command} ${args.join(' ')}`);
}
}
function commandExists(command) {
const result = spawnSync(command, ['--version'], {
stdio: 'ignore',
env: {
...process.env,
PATH: `/usr/local/go/bin:${process.env.PATH ?? ''}`,
},
});
if (result.error && result.error.code === 'ENOENT') {
return false;
}
return (result.status ?? 0) === 0;
}
for (const mapping of schemaFiles) {
const schemaFile = mapping.schema;
const sample = mapping.sample;
const schemaPath = join(schemasDir, schemaFile);
const samplePath = join(fixturesDir, sample);
const schemaJson = JSON.parse(readFileSync(schemaPath, 'utf8'));
const sampleJson = JSON.parse(readFileSync(samplePath, 'utf8'));
const schemaId = schemaJson.$id || ('https://stella-ops.org/schemas/attestor/' + schemaFile);
ajv.removeSchema(schemaId);
ajv.addSchema(schemaJson, schemaId);
const alias = new URL('attestation-common.v1.schema.json', new URL(schemaId));
if (!ajv.getSchema(alias.href)) {
ajv.addSchema(commonSchema, alias.href);
}
const validate = ajv.getSchema(schemaId) || ajv.compile(schemaJson);
const valid = validate(sampleJson);
if (!valid) {
failed = true;
console.error('✖ ' + schemaFile + ' failed for fixture ' + sample);
console.error(validate.errors || []);
} else {
const canonical = stableStringify(sampleJson);
const digest = Buffer.from(canonical, 'utf8').toString('base64');
console.log('✔ ' + schemaFile + ' ✓ ' + sample + ' (canonical b64: ' + digest.slice(0, 16) + '… )');
}
}
if (failed) {
console.error('One or more schema validations failed.');
process.exit(1);
}
try {
console.log('\n▶ Installing TypeScript dependencies...');
runCommand('npm', ['install', '--no-fund', '--no-audit'], { cwd: tsDir });
console.log('▶ Running TypeScript build/tests...');
runCommand('npm', ['run', 'test'], { cwd: tsDir });
const goCandidates = [
'go',
'/usr/local/go/bin/go',
process.env.GO || '',
].filter(Boolean);
const goCommand = goCandidates.find((candidate) => commandExists(candidate));
if (goCommand) {
console.log('▶ Running Go tests...');
const goEnv = {
...process.env,
PATH: `/usr/local/go/bin:${process.env.PATH ?? ''}`,
};
runCommand(goCommand, ['test', './...'], { cwd: goDir, env: goEnv });
} else {
console.warn('⚠️ Go toolchain not found; skipping Go SDK tests.');
}
} catch (err) {
console.error(err.message);
process.exit(1);
}
console.log('All attestation schemas and SDKs validated successfully.');