Files
git.stella-ops.org/src/Web/StellaOps.Web/scan-pages.mjs
master 07cdba01cd feat(web): integration hub audit links + dashboard tips + e2e
Sprint SPRINT_20260415_002_FE_integration_audit_links_and_dashboard_tips.

- integration-hub: integration-detail component + spec with audit links.
- dashboard-v3: component + specs (core/testing + tests/dashboard) with
  getting-started tips.
- audit-log: audit-log-table component + spec.
- policy-governance: client + scope helper.
- deploy-diff: panel + page + service.
- graph: graph-filters component.
- jobengine: scheduler-workers-panel component.
- reachability: reachability-center + witness page components.
- release-investigation: release-investigation-context.
- E2E: audit-consolidation, integrations, policy-orchestrator specs +
  live-auth fixture; live-frontdoor-auth script + playwright outputs.
- Utility scripts: debug-auth, probe-services, scan-pages.
- package.json tweaks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:44:06 +03:00

115 lines
3.9 KiB
JavaScript

import { chromium } from 'playwright';
const BASE = 'http://127.1.0.5';
const USERNAME = process.env.STELLAOPS_FRONTDOOR_USERNAME?.trim()
|| process.env.STELLAOPS_ADMIN_USER?.trim()
|| 'admin';
const PASSWORD = process.env.STELLAOPS_FRONTDOOR_PASSWORD?.trim()
|| process.env.STELLAOPS_ADMIN_PASS?.trim();
const routes = [
'/security',
'/security/findings',
'/security/exceptions',
'/security/vex',
'/security/vulnerabilities',
'/operations/scheduler',
'/operations/doctor',
'/operations/feeds',
'/operations/notifications',
'/operations/health',
'/evidence/bundles',
'/evidence/export',
'/releases',
'/releases/environments',
'/approvals',
'/policy',
'/policy/governance',
'/triage',
'/sources',
'/analytics',
'/settings/admin',
];
(async () => {
if (!PASSWORD) {
throw new Error('Set STELLAOPS_FRONTDOOR_PASSWORD or STELLAOPS_ADMIN_PASS before running this script.');
}
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
// Step 1: Sign in
console.log('=== SIGNING IN ===');
await page.goto(BASE + '/', { waitUntil: 'networkidle', timeout: 15000 });
// Click sign in button
const signInBtn = page.locator('button:has-text("Sign In"), a:has-text("Sign In"), [routerLink*="auth"]').first();
try {
await signInBtn.click({ timeout: 5000 });
} catch {
await page.goto(BASE + '/auth/login', { waitUntil: 'networkidle', timeout: 10000 });
}
await page.waitForTimeout(2000);
console.log('Login page URL: ' + page.url());
try {
const usernameInput = page.locator('input[name="Username"], input[name="username"], input[type="text"]').first();
const passwordInput = page.locator('input[name="Password"], input[name="password"], input[type="password"]').first();
await usernameInput.fill(USERNAME, { timeout: 5000 });
await passwordInput.fill(PASSWORD);
const loginBtn = page.locator('button[type="submit"], button:has-text("Log in"), button:has-text("Login"), button:has-text("Sign in")').first();
await loginBtn.click();
await page.waitForTimeout(3000);
console.log('After login URL: ' + page.url());
} catch (e) {
console.log('Login form error: ' + e.message);
}
await page.waitForTimeout(2000);
console.log('Final URL after auth: ' + page.url());
// Step 2: Navigate to each route using pushState
console.log('\n=== PAGE SCAN (with fresh token) ===');
for (const route of routes) {
const apiCalls = [];
const handler = (response) => {
const url = response.url();
if (!url.includes('.js') && !url.includes('.css') && !url.includes('.ico') &&
!url.includes('.png') && !url.includes('.svg') && !url.includes('.woff') &&
!url.includes('/config.json') && !url.includes('.html') &&
!url.startsWith('data:') && url.startsWith(BASE)) {
const path = new URL(url).pathname;
if (path.startsWith('/api/') || path.startsWith('/v1/') || path.startsWith('/platform/') ||
path.startsWith('/scanner/') || path.startsWith('/policy/') || path.startsWith('/scheduler/') ||
path.startsWith('/doctor/') || path.startsWith('/authority/') || path.startsWith('/console/') ||
path.startsWith('/concelier/') || path.startsWith('/attestor/') || path.startsWith('/analytics') ||
path.startsWith('/health')) {
apiCalls.push({ path, status: response.status() });
}
}
};
page.on('response', handler);
await page.evaluate((r) => {
window.history.pushState({}, '', r);
window.dispatchEvent(new PopStateEvent('popstate'));
}, route);
await page.waitForTimeout(3000);
page.removeListener('response', handler);
const callSummary = apiCalls.map(c => c.status + ' ' + c.path).join(', ') || 'NO API CALLS';
console.log(route + ': ' + callSummary);
}
await browser.close();
})();