feat: Add Bun language analyzer and related functionality

- Implemented BunPackageNormalizer to deduplicate packages by name and version.
- Created BunProjectDiscoverer to identify Bun project roots in the filesystem.
- Added project files for the Bun analyzer including manifest and project configuration.
- Developed comprehensive tests for Bun language analyzer covering various scenarios.
- Included fixture files for testing standard installs, isolated linker installs, lockfile-only scenarios, and workspaces.
- Established stubs for authentication sessions to facilitate testing in the web application.
This commit is contained in:
StellaOps Bot
2025-12-06 11:20:35 +02:00
parent b978ae399f
commit a7cd10020a
85 changed files with 7414 additions and 42 deletions

View File

@@ -10,7 +10,7 @@
| WEB-TEN-47-CONTRACT | DONE (2025-12-01) | Gateway tenant auth/ABAC contract doc v1.0 published (`docs/api/gateway/tenant-auth.md`). |
| WEB-VULN-29-LEDGER-DOC | DONE (2025-12-01) | Findings Ledger proxy contract doc v1.0 with idempotency + retries (`docs/api/gateway/findings-ledger-proxy.md`). |
| WEB-RISK-68-NOTIFY-DOC | DONE (2025-12-01) | Notifications severity transition event schema v1.0 published (`docs/api/gateway/notifications-severity.md`). |
| UI-MICRO-GAPS-0209-011 | DOING (2025-12-04) | Motion token catalog + Storybook/Playwright a11y harness added; remaining work: component mapping, perf budgets, deterministic snapshots. |
| UI-MICRO-GAPS-0209-011 | BLOCKED (2025-12-06) | Motion token catalog + Storybook/Playwright a11y harness added; remaining work paused pending SIG-26 reachability fixtures and final token mapping approvals. |
| UI-POLICY-20-001 | DONE (2025-12-05) | Policy Studio Monaco editor with DSL highlighting, lint markers, and compliance checklist shipped. |
| UI-POLICY-20-002 | DONE (2025-12-05) | Simulation panel with deterministic diff rendering shipped (`/policy-studio/packs/:packId/simulate`). |
| UI-POLICY-20-003 | DONE (2025-12-05) | Approvals workflow UI delivered with submit/review actions, two-person badge, and deterministic log. |

View File

@@ -24,10 +24,29 @@
Loading console context…
</div>
<ng-container *ngIf="!loading()">
<section class="console-profile__card" *ngIf="profile() as profile">
<header>
<h2>User Profile</h2>
<ng-container *ngIf="!loading()">
<section class="console-profile__card console-profile__callout">
<header>
<h2>Policy Studio roles & scopes</h2>
</header>
<ul>
<li><strong>Author</strong>: policy:read, policy:author, policy:edit, policy:submit, policy:simulate</li>
<li><strong>Reviewer</strong>: policy:read, policy:review, policy:simulate</li>
<li><strong>Approver</strong>: policy:read, policy:review, policy:approve, policy:simulate</li>
<li><strong>Operator</strong>: policy:read, policy:operate, policy:activate, policy:run, policy:simulate</li>
<li><strong>Audit</strong>: policy:read, policy:audit</li>
</ul>
<p class="console-profile__hint">
Use this list to verify your token covers the flows you need (editor, simulate, approvals, dashboard, audit exports).
</p>
<p class="console-profile__hint">
For Cypress/e2e, load stub sessions from <code>testing/auth-fixtures.ts</code> (author/reviewer/approver/operator/audit) and seed <code>AuthSessionStore</code> before navigating.
</p>
</section>
<section class="console-profile__card" *ngIf="profile() as profile">
<header>
<h2>User Profile</h2>
<span class="tenant-chip">
Tenant
<strong>{{ profile.tenant }}</strong>

View File

@@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common';
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { ComponentFixture, TestBed, fakeAsync, tick, flushMicrotasks } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, convertToParamMap } from '@angular/router';
import { of } from 'rxjs';
@@ -12,7 +12,7 @@ describe('PolicyDashboardComponent', () => {
let component: PolicyDashboardComponent;
let api: jasmine.SpyObj<PolicyApiService>;
beforeEach(async () => {
beforeEach(fakeAsync(() => {
api = jasmine.createSpyObj<PolicyApiService>('PolicyApiService', ['getRunDashboard']);
api.getRunDashboard.and.returnValue(
@@ -47,7 +47,7 @@ describe('PolicyDashboardComponent', () => {
}) as any
);
await TestBed.configureTestingModule({
TestBed.configureTestingModule({
imports: [CommonModule, ReactiveFormsModule, PolicyDashboardComponent],
providers: [
{ provide: PolicyApiService, useValue: api },
@@ -63,9 +63,11 @@ describe('PolicyDashboardComponent', () => {
],
}).compileComponents();
flushMicrotasks();
fixture = TestBed.createComponent(PolicyDashboardComponent);
component = fixture.componentInstance;
});
}));
it('sorts runs descending by completedAt', fakeAsync(() => {
fixture.detectChanges();

View File

@@ -0,0 +1,45 @@
export type StubAuthSession = {
subjectId: string;
tenant: string;
scopes: string[];
};
const baseScopes = ['ui.read', 'policy:read'];
export const policyAuthorSession: StubAuthSession = {
subjectId: 'user-author',
tenant: 'tenant-default',
scopes: [...baseScopes, 'policy:author', 'policy:edit', 'policy:submit', 'policy:simulate'],
};
export const policyReviewerSession: StubAuthSession = {
subjectId: 'user-reviewer',
tenant: 'tenant-default',
scopes: [...baseScopes, 'policy:review', 'policy:simulate'],
};
export const policyApproverSession: StubAuthSession = {
subjectId: 'user-approver',
tenant: 'tenant-default',
scopes: [...baseScopes, 'policy:review', 'policy:approve', 'policy:simulate'],
};
export const policyOperatorSession: StubAuthSession = {
subjectId: 'user-operator',
tenant: 'tenant-default',
scopes: [...baseScopes, 'policy:operate', 'policy:activate', 'policy:run', 'policy:simulate'],
};
export const policyAuditSession: StubAuthSession = {
subjectId: 'user-auditor',
tenant: 'tenant-default',
scopes: [...baseScopes, 'policy:audit'],
};
export const allPolicySessions = [
policyAuthorSession,
policyReviewerSession,
policyApproverSession,
policyOperatorSession,
policyAuditSession,
];

View File

@@ -0,0 +1,35 @@
import { AuthSessionStore } from '../core/auth/auth-session.store';
import { AuthSession } from '../core/auth/auth-session.model';
import { StubAuthSession } from './auth-fixtures';
/**
* Seed the AuthSessionStore with a deterministic stub session for tests/e2e.
* Populates tokens/identity using the provided scopes/tenant/subject and
* sets a long-lived expiry to avoid refresh churn in short-lived test runs.
*/
export function seedAuthSession(store: AuthSessionStore, stub: StubAuthSession): void {
const now = Date.now();
const session: AuthSession = {
tokens: {
accessToken: 'stub-token-' + stub.subjectId,
expiresAtEpochMs: now + 60 * 60 * 1000,
tokenType: 'Bearer',
scope: stub.scopes.join(' '),
},
identity: {
subject: stub.subjectId,
name: stub.subjectId,
roles: [],
},
dpopKeyThumbprint: 'stub-dpop-' + stub.subjectId,
issuedAtEpochMs: now,
tenantId: stub.tenant,
scopes: stub.scopes,
audiences: ['stellaops'],
authenticationTimeEpochMs: now,
freshAuthActive: true,
freshAuthExpiresAtEpochMs: now + 30 * 60 * 1000,
};
store.setSession(session);
}

View File

@@ -0,0 +1,6 @@
export * from './auth-fixtures';
export * from './auth-store.stub';
export * from './exception-fixtures';
export * from './notify-fixtures';
export * from './policy-fixtures';
export * from './scan-fixtures';