Implement VEX document verification system with issuer management and signature verification

- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation.
- Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments.
- Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats.
- Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats.
- Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction.
- Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
StellaOps Bot
2025-12-06 13:41:22 +02:00
parent 2141196496
commit 5e514532df
112 changed files with 24861 additions and 211 deletions

View File

@@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
import type * as Monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker&inline';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker&inline';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker&inline';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker&inline';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker&inline';
import {
defineStellaDslTheme,

View File

@@ -64,12 +64,11 @@ describe('PolicyEditorComponent', () => {
fixture.detectChanges();
});
it('loads pack content into the editor model', fakeAsync(() => {
tick();
it('loads pack content into the editor model', () => {
expect(monacoLoader.model?.getValue()).toContain('package "demo"');
}));
});
it('applies lint diagnostics as Monaco markers', fakeAsync(() => {
it('applies lint diagnostics as Monaco markers', () => {
const lintResult = {
valid: false,
errors: [
@@ -89,11 +88,10 @@ describe('PolicyEditorComponent', () => {
policyApi.lint.and.returnValue(of(lintResult) as any);
component.triggerLint();
tick();
expect(monacoLoader.lastMarkers.length).toBe(1);
expect(monacoLoader.lastMarkers[0].message).toContain('Missing rule header');
}));
});
});
class MonacoLoaderStub {
@@ -102,34 +100,7 @@ class MonacoLoaderStub {
lastMarkers: Monaco.editor.IMarkerData[] = [];
load = jasmine.createSpy('load').and.callFake(async () => {
const self = this;
return {
editor: {
createModel: (value: string) => {
this.model = new FakeModel(value);
this.editor = new FakeEditor(this.model);
return this.model as unknown as Monaco.editor.ITextModel;
},
create: () => this.editor as unknown as Monaco.editor.IStandaloneCodeEditor,
setModelMarkers: (
_model: Monaco.editor.ITextModel,
_owner: string,
markers: Monaco.editor.IMarkerData[]
) => {
self.lastMarkers = markers;
},
},
languages: {
register: () => undefined,
setMonarchTokensProvider: () => undefined,
setLanguageConfiguration: () => undefined,
},
MarkerSeverity: {
Error: 8,
Warning: 4,
Info: 2,
},
} as unknown as MonacoNamespace;
return mockMonaco(this);
});
}
@@ -173,3 +144,27 @@ class FakeEditor {
}
type MonacoNamespace = typeof import('monaco-editor');
function mockMonaco(loader: MonacoLoaderStub): MonacoNamespace {
const severity = { Error: 8, Warning: 4, Info: 2 };
return {
editor: {
createModel: (value: string) => {
loader.model = new FakeModel(value);
loader.editor = new FakeEditor(loader.model);
return loader.model as unknown as Monaco.editor.ITextModel;
},
create: () => loader.editor as unknown as Monaco.editor.IStandaloneCodeEditor,
setModelMarkers: (_model: Monaco.editor.ITextModel, _owner: string, markers: Monaco.editor.IMarkerData[]) => {
loader.lastMarkers = markers;
},
setTheme: () => undefined,
},
languages: {
register: () => undefined,
setMonarchTokensProvider: () => undefined,
setLanguageConfiguration: () => undefined,
},
MarkerSeverity: severity as unknown as Monaco.editor.IMarkerSeverity,
} as unknown as MonacoNamespace;
}

View File

@@ -1,4 +1,4 @@
{
"status": "passed",
"failedTests": []
{
"status": "interrupted",
"failedTests": []
}

View File

@@ -1,4 +1,5 @@
import { expect, test } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { policyAuthorSession } from '../src/app/testing';
const mockConfig = {
authority: {
@@ -24,7 +25,7 @@ const mockConfig = {
},
};
test.beforeEach(async ({ page }) => {
test.beforeEach(async ({ page }) => {
page.on('console', (message) => {
// bubble up browser logs for debugging
console.log('[browser]', message.type(), message.text());
@@ -32,7 +33,7 @@ test.beforeEach(async ({ page }) => {
page.on('pageerror', (error) => {
console.log('[pageerror]', error.message);
});
await page.addInitScript(() => {
await page.addInitScript(() => {
// Capture attempted redirects so the test can assert against them.
(window as any).__stellaopsAssignedUrls = [];
const originalAssign = window.location.assign.bind(window.location);
@@ -40,8 +41,10 @@ test.beforeEach(async ({ page }) => {
(window as any).__stellaopsAssignedUrls.push(url.toString());
};
window.sessionStorage.clear();
});
window.sessionStorage.clear();
// Seed a default Policy Studio author session so guarded routes load in e2e
(window as any).__stellaopsTestSession = policyAuthorSession;
});
await page.route('**/config.json', (route) =>
route.fulfill({
status: 200,