docs: define auth broker p0 contract
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
type RunnerDisposition = "ready" | "infra-blocked" | "business-failed";
|
||||
|
||||
interface FailureContract {
|
||||
failureKind: string;
|
||||
httpStatus: number;
|
||||
runnerDisposition: RunnerDisposition;
|
||||
retryable: boolean;
|
||||
}
|
||||
|
||||
const docPath = "docs/reference/auth-broker.md";
|
||||
const doc = readFileSync(docPath, "utf8");
|
||||
|
||||
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
||||
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
||||
}
|
||||
|
||||
function assertDocContains(text: string): void {
|
||||
assertCondition(doc.includes(text), `missing auth broker doc text: ${text}`);
|
||||
}
|
||||
|
||||
const requiredOperations = [
|
||||
"github.auth.status",
|
||||
"github.issue.list",
|
||||
"github.issue.read",
|
||||
"github.pr.list",
|
||||
"github.pr.read",
|
||||
"github.pr.create",
|
||||
"github.pr.comment.create",
|
||||
];
|
||||
|
||||
const forbiddenBoundaries = [
|
||||
"gh pr merge",
|
||||
"arbitrary `gh api`",
|
||||
"Docker registry login",
|
||||
"deploy commands",
|
||||
];
|
||||
|
||||
const requiredAuditFields = [
|
||||
"requestId",
|
||||
"observedAt",
|
||||
"caller.plane",
|
||||
"operation",
|
||||
"repo",
|
||||
"credentialRef",
|
||||
"credentialValuePrinted",
|
||||
"runnerDisposition",
|
||||
"retryable",
|
||||
];
|
||||
|
||||
const failureContracts: FailureContract[] = [
|
||||
{ failureKind: "auth-not-configured", httpStatus: 503, runnerDisposition: "infra-blocked", retryable: false },
|
||||
{ failureKind: "broker-unavailable", httpStatus: 503, runnerDisposition: "infra-blocked", retryable: true },
|
||||
{ failureKind: "unauthorized-caller", httpStatus: 403, runnerDisposition: "infra-blocked", retryable: false },
|
||||
{ failureKind: "repo-not-allowed", httpStatus: 403, runnerDisposition: "business-failed", retryable: false },
|
||||
{ failureKind: "operation-not-allowed", httpStatus: 403, runnerDisposition: "business-failed", retryable: false },
|
||||
{ failureKind: "dry-run-required", httpStatus: 409, runnerDisposition: "business-failed", retryable: false },
|
||||
{ failureKind: "validation-failed", httpStatus: 400, runnerDisposition: "business-failed", retryable: false },
|
||||
{ failureKind: "github-egress-failed", httpStatus: 502, runnerDisposition: "infra-blocked", retryable: true },
|
||||
{ failureKind: "github-rate-limited", httpStatus: 429, runnerDisposition: "infra-blocked", retryable: true },
|
||||
{ failureKind: "github-permission-denied", httpStatus: 403, runnerDisposition: "infra-blocked", retryable: false },
|
||||
{ failureKind: "scope-insufficient", httpStatus: 403, runnerDisposition: "infra-blocked", retryable: false },
|
||||
{ failureKind: "repo-not-found", httpStatus: 404, runnerDisposition: "business-failed", retryable: false },
|
||||
{ failureKind: "upstream-invalid-response", httpStatus: 502, runnerDisposition: "infra-blocked", retryable: true },
|
||||
];
|
||||
|
||||
const samplePreflightResponse = {
|
||||
ok: true,
|
||||
runnerDisposition: "ready" as const,
|
||||
failureKind: null,
|
||||
degradedReason: null,
|
||||
tokenCoverage: {
|
||||
ok: true,
|
||||
source: "auth-broker",
|
||||
scope: "broker-held-github-credential",
|
||||
runnerEnvTokenRequired: false,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
prCapabilityContract: {
|
||||
targetBranch: "master",
|
||||
systemGhBinaryRequiredForWrites: false,
|
||||
preflightCreatesPr: false,
|
||||
preflightMergesPr: false,
|
||||
brokerProxy: {
|
||||
ok: true,
|
||||
operations: ["github.auth.status", "github.pr.create"],
|
||||
writesRemote: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function walk(value: unknown, path: string[] = []): void {
|
||||
if (typeof value === "string") {
|
||||
assertCondition(!/gh[pousr]_[A-Za-z0-9_]{20,}/u.test(value), "sample must not contain GitHub token-like values", { path, value });
|
||||
assertCondition(!/github_pat_[A-Za-z0-9_]+/u.test(value), "sample must not contain GitHub PAT-like values", { path, value });
|
||||
assertCondition(!/^Bearer\s+/iu.test(value), "sample must not contain Authorization header values", { path, value });
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item, index) => walk(item, [...path, String(index)]));
|
||||
return;
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
for (const [key, entry] of Object.entries(value)) {
|
||||
assertCondition(!["token", "secret", "authorization", "cookie"].includes(key.toLowerCase()), "sample must not expose secret-bearing keys", { path, key });
|
||||
walk(entry, [...path, key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
for (const heading of ["## Existing Paths", "## API", "## Permission Boundary", "## Audit Fields", "## Failure Semantics", "## D601 Dev Acceptance"]) {
|
||||
assertDocContains(heading);
|
||||
}
|
||||
for (const path of [
|
||||
"scripts/src/gh.ts",
|
||||
"scripts/code-queue-pr-preflight-example.ts",
|
||||
"src/components/microservices/code-queue/src/runtime-preflight.ts",
|
||||
"scripts/src/code-queue.ts",
|
||||
"src/components/microservices/code-queue/src/index.ts",
|
||||
"src/components/microservices/code-queue/docker-compose.d601.yml",
|
||||
"src/components/microservices/code-queue/Dockerfile",
|
||||
]) {
|
||||
assertDocContains(path);
|
||||
}
|
||||
for (const operation of requiredOperations) assertDocContains(operation);
|
||||
for (const boundary of forbiddenBoundaries) assertDocContains(boundary);
|
||||
for (const field of requiredAuditFields) assertDocContains(field);
|
||||
for (const failure of failureContracts) {
|
||||
assertDocContains(failure.failureKind);
|
||||
assertCondition(Number.isInteger(failure.httpStatus) && failure.httpStatus >= 400, "failure HTTP status must be an error status", failure);
|
||||
}
|
||||
assertCondition(new Set(failureContracts.map((item) => item.failureKind)).size === failureContracts.length, "failure kinds must be unique", failureContracts);
|
||||
assertCondition(samplePreflightResponse.tokenCoverage.source === "auth-broker", "preflight should use broker token coverage", samplePreflightResponse.tokenCoverage);
|
||||
assertCondition(samplePreflightResponse.tokenCoverage.runnerEnvTokenRequired === false, "runner env token must not be required", samplePreflightResponse.tokenCoverage);
|
||||
assertCondition(samplePreflightResponse.prCapabilityContract.brokerProxy.writesRemote === false, "P0 preflight must not write remotely", samplePreflightResponse.prCapabilityContract);
|
||||
assertCondition(samplePreflightResponse.prCapabilityContract.preflightMergesPr === false, "P0 preflight must not merge PRs", samplePreflightResponse.prCapabilityContract);
|
||||
walk(samplePreflightResponse);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
docPath,
|
||||
operations: requiredOperations,
|
||||
failureKinds: failureContracts.map((item) => item.failureKind),
|
||||
p0Safety: {
|
||||
runnerEnvTokenRequired: false,
|
||||
preflightWritesRemote: false,
|
||||
secretValuesPrinted: false,
|
||||
liveWritesDefault: "dry-run-required",
|
||||
},
|
||||
}, null, 2)}\n`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user