Make registry-admin audit route self-identifying

This commit is contained in:
master
2026-03-11 19:09:46 +02:00
parent 6afd8f951e
commit 8eec0a9dee
5 changed files with 308 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env node
import { mkdirSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { chromium } from 'playwright';
import { authenticateFrontdoor, createAuthenticatedContext } from './live-frontdoor-auth.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const webRoot = path.resolve(__dirname, '..');
const outputDirectory = path.join(webRoot, 'output', 'playwright');
const outputPath = path.join(outputDirectory, 'live-registry-admin-audit-check.json');
const baseUrl = process.env.STELLAOPS_FRONTDOOR_BASE_URL?.trim() || 'https://stella-ops.local';
const statePath = path.join(outputDirectory, 'live-frontdoor-auth-state.json');
function trimText(value, maxLength = 400) {
const normalized = value.replace(/\s+/g, ' ').trim();
return normalized.length > maxLength ? `${normalized.slice(0, maxLength)}...` : normalized;
}
async function collectHeadings(page) {
return page.locator('h1, main h1, main h2, h2').evaluateAll((elements) =>
elements
.map((element) => (element.textContent || '').replace(/\s+/g, ' ').trim())
.filter((text, index, values) => text.length > 0 && values.indexOf(text) === index),
).catch(() => []);
}
async function main() {
mkdirSync(outputDirectory, { recursive: true });
const authReport = await authenticateFrontdoor({
statePath,
reportPath: path.join(outputDirectory, 'live-frontdoor-auth-report.json'),
});
const browser = await chromium.launch({
headless: true,
args: ['--disable-dev-shm-usage'],
});
const context = await createAuthenticatedContext(browser, authReport, { statePath });
const page = await context.newPage();
const report = {
generatedAtUtc: new Date().toISOString(),
baseUrl,
startUrl: `${baseUrl}/ops/integrations/registry-admin?tenant=demo-prod&regions=apac,eu-west,us-east,us-west`,
finalUrl: '',
headings: [],
auditHeadingMatched: false,
actionOk: false,
errors: [],
};
page.on('console', (message) => {
if (message.type() === 'error') {
report.errors.push(`console:${message.text()}`);
}
});
page.on('pageerror', (error) => {
report.errors.push(`page:${error.message}`);
});
try {
await page.goto(report.startUrl, {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForTimeout(4_000);
const auditTab = page.locator('nav[role="tablist"] a:has-text("Audit Log")').first();
await auditTab.click();
await page.waitForTimeout(4_000);
report.finalUrl = page.url();
report.headings = await collectHeadings(page);
const bodyText = trimText(await page.locator('body').innerText().catch(() => ''), 1200);
report.auditHeadingMatched = report.headings.some((heading) => /audit/i.test(heading))
|| /registry audit log/i.test(bodyText);
report.actionOk = report.finalUrl.includes('/registry-admin/audit')
&& report.finalUrl.includes('tenant=')
&& report.finalUrl.includes('regions=')
&& report.auditHeadingMatched;
await page.screenshot({
path: path.join(outputDirectory, 'live-registry-admin-audit-check.png'),
fullPage: true,
}).catch(() => {});
} finally {
await browser.close();
}
writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
if (!report.actionOk || report.errors.length > 0) {
process.exitCode = 1;
}
}
if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
main().catch((error) => {
process.stderr.write(`[live-registry-admin-audit-check] ${error instanceof Error ? error.message : String(error)}\n`);
process.exit(1);
});
}