@biffo/cli 0.303.1 → 0.303.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +418 -180
- package/package.json +1 -1
- package/scripts/verify.sh +1 -0
package/dist/index.js
CHANGED
|
@@ -10920,6 +10920,239 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
|
|
|
10920
10920
|
console.log("\u2713 ADR numbering guard: OK");
|
|
10921
10921
|
}
|
|
10922
10922
|
|
|
10923
|
+
// src/lib/api-gateway-integration-guard.ts
|
|
10924
|
+
import { readFileSync as readFileSync30, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
|
|
10925
|
+
import { join as join41 } from "path";
|
|
10926
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
|
|
10927
|
+
var MODULE_TYPE = "module";
|
|
10928
|
+
var INTEGRATION_TYPE = "aws_apigatewayv2_integration";
|
|
10929
|
+
function lineNumber(text, index) {
|
|
10930
|
+
let count = 1;
|
|
10931
|
+
for (let i = 0; i < index && i < text.length; i++) {
|
|
10932
|
+
if (text[i] === "\n") count += 1;
|
|
10933
|
+
}
|
|
10934
|
+
return count;
|
|
10935
|
+
}
|
|
10936
|
+
function balancedBraceSpan(text, openIndex) {
|
|
10937
|
+
let depth = 0;
|
|
10938
|
+
for (let i = openIndex; i < text.length; i++) {
|
|
10939
|
+
if (text[i] === "{") depth += 1;
|
|
10940
|
+
else if (text[i] === "}") {
|
|
10941
|
+
depth -= 1;
|
|
10942
|
+
if (depth === 0) return i;
|
|
10943
|
+
}
|
|
10944
|
+
}
|
|
10945
|
+
return null;
|
|
10946
|
+
}
|
|
10947
|
+
function findResourceBlocks(text, file, resourceType) {
|
|
10948
|
+
const blocks = [];
|
|
10949
|
+
const pattern = new RegExp(`resource\\s+"${resourceType}"\\s+"([\\w-]+)"\\s*\\{`, "g");
|
|
10950
|
+
let m;
|
|
10951
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
10952
|
+
const name = m[1];
|
|
10953
|
+
const line = lineNumber(text, m.index);
|
|
10954
|
+
const openIndex = m.index + m[0].length - 1;
|
|
10955
|
+
const closeIndex = balancedBraceSpan(text, openIndex);
|
|
10956
|
+
if (closeIndex === null) {
|
|
10957
|
+
blocks.push({ file, line, type: resourceType, name, body: null });
|
|
10958
|
+
continue;
|
|
10959
|
+
}
|
|
10960
|
+
blocks.push({
|
|
10961
|
+
file,
|
|
10962
|
+
line,
|
|
10963
|
+
type: resourceType,
|
|
10964
|
+
name,
|
|
10965
|
+
body: text.slice(openIndex + 1, closeIndex)
|
|
10966
|
+
});
|
|
10967
|
+
}
|
|
10968
|
+
return blocks;
|
|
10969
|
+
}
|
|
10970
|
+
function findModuleBlocks(text, file) {
|
|
10971
|
+
const blocks = [];
|
|
10972
|
+
const pattern = /module\s+"([\w-]+)"\s*\{/g;
|
|
10973
|
+
let m;
|
|
10974
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
10975
|
+
const name = m[1];
|
|
10976
|
+
const line = lineNumber(text, m.index);
|
|
10977
|
+
const openIndex = m.index + m[0].length - 1;
|
|
10978
|
+
const closeIndex = balancedBraceSpan(text, openIndex);
|
|
10979
|
+
if (closeIndex === null) {
|
|
10980
|
+
blocks.push({ file, line, type: MODULE_TYPE, name, body: null });
|
|
10981
|
+
continue;
|
|
10982
|
+
}
|
|
10983
|
+
blocks.push({
|
|
10984
|
+
file,
|
|
10985
|
+
line,
|
|
10986
|
+
type: MODULE_TYPE,
|
|
10987
|
+
name,
|
|
10988
|
+
body: text.slice(openIndex + 1, closeIndex)
|
|
10989
|
+
});
|
|
10990
|
+
}
|
|
10991
|
+
return blocks;
|
|
10992
|
+
}
|
|
10993
|
+
function countRawResourceDeclarations(text, resourceType) {
|
|
10994
|
+
const needle = resourceType === MODULE_TYPE ? 'module "' : `resource "${resourceType}"`;
|
|
10995
|
+
return text.split(needle).length - 1;
|
|
10996
|
+
}
|
|
10997
|
+
function walkTerraformFiles(root) {
|
|
10998
|
+
const out = [];
|
|
10999
|
+
const walk2 = (dir) => {
|
|
11000
|
+
let entries;
|
|
11001
|
+
try {
|
|
11002
|
+
entries = readdirSync16(dir);
|
|
11003
|
+
} catch {
|
|
11004
|
+
return;
|
|
11005
|
+
}
|
|
11006
|
+
for (const entry of entries) {
|
|
11007
|
+
const p = join41(dir, entry);
|
|
11008
|
+
let st;
|
|
11009
|
+
try {
|
|
11010
|
+
st = statSync9(p);
|
|
11011
|
+
} catch {
|
|
11012
|
+
continue;
|
|
11013
|
+
}
|
|
11014
|
+
if (st.isDirectory()) {
|
|
11015
|
+
if (SKIP_DIRS.has(entry)) continue;
|
|
11016
|
+
walk2(p);
|
|
11017
|
+
continue;
|
|
11018
|
+
}
|
|
11019
|
+
if (entry.endsWith(".tf")) out.push(p);
|
|
11020
|
+
}
|
|
11021
|
+
};
|
|
11022
|
+
walk2(root);
|
|
11023
|
+
return out.sort();
|
|
11024
|
+
}
|
|
11025
|
+
function isApiGatewaySource(body) {
|
|
11026
|
+
const m = /source\s*=\s*"([^"]+)"/.exec(body);
|
|
11027
|
+
if (!m) return false;
|
|
11028
|
+
return /(^|\/)modules\/cloud\/aws\/api-gateway\/?$/.test(m[1].trim());
|
|
11029
|
+
}
|
|
11030
|
+
function protectedFunctionModuleName(body) {
|
|
11031
|
+
const m = /lambda_function_arn\s*=\s*module\.([\w-]+)\.function_arn\b/.exec(body);
|
|
11032
|
+
return m ? m[1] : null;
|
|
11033
|
+
}
|
|
11034
|
+
function integrationUriOf(body) {
|
|
11035
|
+
const m = /integration_uri\s*=\s*(.+)/.exec(body);
|
|
11036
|
+
if (!m) return null;
|
|
11037
|
+
return m[1].trim().replace(/^"\$\{(.+)\}"$/, "$1").replace(/^"(.+)"$/, "$1").trim();
|
|
11038
|
+
}
|
|
11039
|
+
function auditApiGatewayIntegrations(root) {
|
|
11040
|
+
const files = walkTerraformFiles(root);
|
|
11041
|
+
if (files.length === 0) {
|
|
11042
|
+
throw new Error(
|
|
11043
|
+
`auditApiGatewayIntegrations: no .tf files found under ${root} \u2014 the guard cannot verify anything against input it cannot see. This is a hard failure, not "0 violations": fix the path, do not treat an empty scan as a clean pass.`
|
|
11044
|
+
);
|
|
11045
|
+
}
|
|
11046
|
+
const moduleBlocks = [];
|
|
11047
|
+
const integrationBlocks = [];
|
|
11048
|
+
let rawModuleCount = 0;
|
|
11049
|
+
let rawIntegrationCount = 0;
|
|
11050
|
+
for (const file of files) {
|
|
11051
|
+
const text = readFileSync30(file, "utf8");
|
|
11052
|
+
rawModuleCount += countRawResourceDeclarations(text, MODULE_TYPE);
|
|
11053
|
+
rawIntegrationCount += countRawResourceDeclarations(text, INTEGRATION_TYPE);
|
|
11054
|
+
moduleBlocks.push(...findModuleBlocks(text, file));
|
|
11055
|
+
integrationBlocks.push(...findResourceBlocks(text, file, INTEGRATION_TYPE));
|
|
11056
|
+
}
|
|
11057
|
+
const unterminatedBlocks = [...moduleBlocks, ...integrationBlocks].filter((b) => b.body === null);
|
|
11058
|
+
const moduleBlind = rawModuleCount > 0 && moduleBlocks.length === 0;
|
|
11059
|
+
const integrationBlind = rawIntegrationCount > 0 && integrationBlocks.length === 0;
|
|
11060
|
+
const protectedLambdas = [];
|
|
11061
|
+
for (const block of moduleBlocks) {
|
|
11062
|
+
if (block.body === null) continue;
|
|
11063
|
+
if (!isApiGatewaySource(block.body)) continue;
|
|
11064
|
+
if (block.file.split("/").includes("_skeletons")) continue;
|
|
11065
|
+
const functionModuleName = protectedFunctionModuleName(block.body);
|
|
11066
|
+
if (functionModuleName === null) continue;
|
|
11067
|
+
protectedLambdas.push({
|
|
11068
|
+
functionModuleName,
|
|
11069
|
+
gatewayModuleName: block.name,
|
|
11070
|
+
file: block.file,
|
|
11071
|
+
line: block.line
|
|
11072
|
+
});
|
|
11073
|
+
}
|
|
11074
|
+
const protectedNames = new Set(protectedLambdas.map((p) => p.functionModuleName));
|
|
11075
|
+
const integrations = [];
|
|
11076
|
+
for (const block of integrationBlocks) {
|
|
11077
|
+
if (block.body === null) continue;
|
|
11078
|
+
integrations.push({
|
|
11079
|
+
file: block.file,
|
|
11080
|
+
line: block.line,
|
|
11081
|
+
name: block.name,
|
|
11082
|
+
integrationUri: integrationUriOf(block.body)
|
|
11083
|
+
});
|
|
11084
|
+
}
|
|
11085
|
+
const violations = [];
|
|
11086
|
+
for (const integration of integrations) {
|
|
11087
|
+
if (integration.integrationUri === null) continue;
|
|
11088
|
+
const m = /^module\.([\w-]+)\.function_arn$/.exec(integration.integrationUri);
|
|
11089
|
+
if (!m) continue;
|
|
11090
|
+
const targetModuleName = m[1];
|
|
11091
|
+
if (!protectedNames.has(targetModuleName)) continue;
|
|
11092
|
+
const owner = protectedLambdas.find((p) => p.functionModuleName === targetModuleName);
|
|
11093
|
+
violations.push({
|
|
11094
|
+
file: integration.file,
|
|
11095
|
+
line: integration.line,
|
|
11096
|
+
integrationName: integration.name,
|
|
11097
|
+
targetModuleName,
|
|
11098
|
+
reason: `aws_apigatewayv2_integration.${integration.name} targets module.${targetModuleName}.function_arn (unqualified), but module.${owner?.gatewayModuleName ?? "<gateway>"} already grants that Lambda's "live" alias \u2014 not the unqualified function \u2014 invoke access (#1747). This integration will fail closed with a generic API Gateway 500 at runtime, invisible to terraform plan/apply on either side. Use module.${owner?.gatewayModuleName ?? "<gateway>"}.lambda_integration_uri instead (biffo-template#1900).`
|
|
11099
|
+
});
|
|
11100
|
+
}
|
|
11101
|
+
const ok = !moduleBlind && !integrationBlind && unterminatedBlocks.length === 0 && violations.length === 0;
|
|
11102
|
+
const summary = `scanned ${files.length} .tf file(s) under ${root}; ${moduleBlocks.length} module block(s) found (${protectedLambdas.length} api-gateway instance(s) fronting an alias-qualified Lambda), ${integrationBlocks.length} ${INTEGRATION_TYPE} block(s) found; ${violations.length} mis-qualified, ${unterminatedBlocks.length} unterminated.`;
|
|
11103
|
+
return {
|
|
11104
|
+
filesScanned: files.length,
|
|
11105
|
+
moduleBlocksFound: moduleBlocks.length,
|
|
11106
|
+
integrationBlocksFound: integrationBlocks.length,
|
|
11107
|
+
protectedLambdas,
|
|
11108
|
+
integrations,
|
|
11109
|
+
unterminatedBlocks,
|
|
11110
|
+
violations,
|
|
11111
|
+
moduleBlind,
|
|
11112
|
+
integrationBlind,
|
|
11113
|
+
ok,
|
|
11114
|
+
summary
|
|
11115
|
+
};
|
|
11116
|
+
}
|
|
11117
|
+
|
|
11118
|
+
// src/scripts/check-api-gateway-integration.ts
|
|
11119
|
+
async function runApiGatewayIntegrationCheck() {
|
|
11120
|
+
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11121
|
+
let report;
|
|
11122
|
+
try {
|
|
11123
|
+
report = auditApiGatewayIntegrations(root);
|
|
11124
|
+
} catch (err) {
|
|
11125
|
+
console.error("\u2717 API Gateway integration-qualifier guard: could not run\n");
|
|
11126
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
11127
|
+
process.exit(1);
|
|
11128
|
+
}
|
|
11129
|
+
console.log(`scanned ${report.filesScanned} .tf file(s) under ${root}`);
|
|
11130
|
+
if (!report.ok) {
|
|
11131
|
+
console.error("\u2717 API Gateway integration-qualifier guard: mis-qualified integration(s) found\n");
|
|
11132
|
+
if (report.moduleBlind) {
|
|
11133
|
+
console.error(
|
|
11134
|
+
' BLIND (module): raw source contains `module "..."` blocks but the block extractor found none \u2014 the extractor broke, this is not evidence there are no modules.'
|
|
11135
|
+
);
|
|
11136
|
+
}
|
|
11137
|
+
if (report.integrationBlind) {
|
|
11138
|
+
console.error(
|
|
11139
|
+
" BLIND (aws_apigatewayv2_integration): raw source contains this resource type but the block extractor found none \u2014 the extractor broke, this is not evidence there are no integrations."
|
|
11140
|
+
);
|
|
11141
|
+
}
|
|
11142
|
+
for (const b of report.unterminatedBlocks) {
|
|
11143
|
+
console.error(
|
|
11144
|
+
` UNTERMINATED ${b.file}:${b.line} ${b.type} "${b.name}" never closes \u2014 cannot verify what it targets or grants.`
|
|
11145
|
+
);
|
|
11146
|
+
}
|
|
11147
|
+
for (const v of report.violations) {
|
|
11148
|
+
console.error(` MIS-QUALIFIED ${v.file}:${v.line} ${v.reason}`);
|
|
11149
|
+
}
|
|
11150
|
+
console.error("\nSee biffo-template#1900.");
|
|
11151
|
+
process.exit(1);
|
|
11152
|
+
}
|
|
11153
|
+
console.log(`\u2713 API Gateway integration-qualifier guard: ${report.summary}`);
|
|
11154
|
+
}
|
|
11155
|
+
|
|
10923
11156
|
// src/scripts/check-branch-protection.ts
|
|
10924
11157
|
import { Octokit as Octokit2 } from "@octokit/rest";
|
|
10925
11158
|
|
|
@@ -11180,18 +11413,18 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
|
|
|
11180
11413
|
}
|
|
11181
11414
|
|
|
11182
11415
|
// src/lib/claim-invocation-parity.ts
|
|
11183
|
-
import { existsSync as existsSync40, readFileSync as
|
|
11184
|
-
import { join as
|
|
11416
|
+
import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync17 } from "fs";
|
|
11417
|
+
import { join as join42 } from "path";
|
|
11185
11418
|
function distributedAgentsDocs(root) {
|
|
11186
11419
|
const docs = [];
|
|
11187
|
-
const own =
|
|
11188
|
-
if (existsSync40(own)) docs.push({ path: "AGENTS.md", text:
|
|
11189
|
-
const skeletons =
|
|
11420
|
+
const own = join42(root, "AGENTS.md");
|
|
11421
|
+
if (existsSync40(own)) docs.push({ path: "AGENTS.md", text: readFileSync31(own, "utf8") });
|
|
11422
|
+
const skeletons = join42(root, "_skeletons");
|
|
11190
11423
|
if (existsSync40(skeletons)) {
|
|
11191
|
-
for (const name of
|
|
11192
|
-
const abs =
|
|
11424
|
+
for (const name of readdirSync17(skeletons).sort()) {
|
|
11425
|
+
const abs = join42(skeletons, name, "AGENTS.md");
|
|
11193
11426
|
if (!existsSync40(abs)) continue;
|
|
11194
|
-
docs.push({ path: `_skeletons/${name}/AGENTS.md`, text:
|
|
11427
|
+
docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync31(abs, "utf8") });
|
|
11195
11428
|
}
|
|
11196
11429
|
}
|
|
11197
11430
|
return docs;
|
|
@@ -11324,12 +11557,12 @@ async function runClaimInvocationCheck() {
|
|
|
11324
11557
|
|
|
11325
11558
|
// src/scripts/check-codeql-suppression.ts
|
|
11326
11559
|
import { existsSync as existsSync41 } from "fs";
|
|
11327
|
-
import { join as
|
|
11560
|
+
import { join as join44, relative as relative8 } from "path";
|
|
11328
11561
|
|
|
11329
11562
|
// src/lib/codeql-suppression-guard.ts
|
|
11330
|
-
import { readdirSync as
|
|
11331
|
-
import { join as
|
|
11332
|
-
var
|
|
11563
|
+
import { readdirSync as readdirSync18, readFileSync as readFileSync32, statSync as statSync10 } from "fs";
|
|
11564
|
+
import { join as join43 } from "path";
|
|
11565
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
11333
11566
|
".git",
|
|
11334
11567
|
".worktrees",
|
|
11335
11568
|
"node_modules",
|
|
@@ -11354,20 +11587,20 @@ function walkSourceFiles(root) {
|
|
|
11354
11587
|
const walk2 = (dir) => {
|
|
11355
11588
|
let entries;
|
|
11356
11589
|
try {
|
|
11357
|
-
entries =
|
|
11590
|
+
entries = readdirSync18(dir);
|
|
11358
11591
|
} catch {
|
|
11359
11592
|
return;
|
|
11360
11593
|
}
|
|
11361
11594
|
for (const entry of entries) {
|
|
11362
|
-
const p =
|
|
11595
|
+
const p = join43(dir, entry);
|
|
11363
11596
|
let st;
|
|
11364
11597
|
try {
|
|
11365
|
-
st =
|
|
11598
|
+
st = statSync10(p);
|
|
11366
11599
|
} catch {
|
|
11367
11600
|
continue;
|
|
11368
11601
|
}
|
|
11369
11602
|
if (st.isDirectory()) {
|
|
11370
|
-
if (
|
|
11603
|
+
if (SKIP_DIRS2.has(entry)) continue;
|
|
11371
11604
|
walk2(p);
|
|
11372
11605
|
continue;
|
|
11373
11606
|
}
|
|
@@ -11385,7 +11618,7 @@ function countSourceFiles(root) {
|
|
|
11385
11618
|
function sweepCodeqlSuppressionComments(root) {
|
|
11386
11619
|
const hits = [];
|
|
11387
11620
|
for (const path of walkSourceFiles(root)) {
|
|
11388
|
-
const text =
|
|
11621
|
+
const text = readFileSync32(path, "utf8");
|
|
11389
11622
|
for (const line of findCodeqlSuppressionComments(text)) {
|
|
11390
11623
|
hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
|
|
11391
11624
|
}
|
|
@@ -11396,7 +11629,7 @@ function sweepCodeqlSuppressionComments(root) {
|
|
|
11396
11629
|
// src/scripts/check-codeql-suppression.ts
|
|
11397
11630
|
async function runCodeqlSuppressionCheck() {
|
|
11398
11631
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11399
|
-
const scanRoot =
|
|
11632
|
+
const scanRoot = join44(root, "cli", "src");
|
|
11400
11633
|
if (!existsSync41(scanRoot)) {
|
|
11401
11634
|
console.log(
|
|
11402
11635
|
"\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
|
|
@@ -11421,8 +11654,8 @@ async function runCodeqlSuppressionCheck() {
|
|
|
11421
11654
|
}
|
|
11422
11655
|
|
|
11423
11656
|
// src/lib/cognito-invite-template-guard.ts
|
|
11424
|
-
import { readdirSync as
|
|
11425
|
-
import { join as
|
|
11657
|
+
import { readdirSync as readdirSync19, readFileSync as readFileSync33, statSync as statSync11 } from "fs";
|
|
11658
|
+
import { join as join45 } from "path";
|
|
11426
11659
|
var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
|
|
11427
11660
|
var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
|
|
11428
11661
|
var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
|
|
@@ -11499,18 +11732,18 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
11499
11732
|
const walk2 = (dir, relative11) => {
|
|
11500
11733
|
let entries;
|
|
11501
11734
|
try {
|
|
11502
|
-
entries =
|
|
11735
|
+
entries = readdirSync19(dir);
|
|
11503
11736
|
} catch {
|
|
11504
11737
|
return;
|
|
11505
11738
|
}
|
|
11506
11739
|
for (const entry of entries) {
|
|
11507
11740
|
if (entry === "node_modules" || entry === ".git" || entry === ".worktrees" || entry === ".venv")
|
|
11508
11741
|
continue;
|
|
11509
|
-
const full =
|
|
11742
|
+
const full = join45(dir, entry);
|
|
11510
11743
|
const rel = `${relative11}/${entry}`;
|
|
11511
11744
|
let isDir;
|
|
11512
11745
|
try {
|
|
11513
|
-
isDir =
|
|
11746
|
+
isDir = statSync11(full).isDirectory();
|
|
11514
11747
|
} catch {
|
|
11515
11748
|
continue;
|
|
11516
11749
|
}
|
|
@@ -11521,12 +11754,12 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
11521
11754
|
}
|
|
11522
11755
|
}
|
|
11523
11756
|
};
|
|
11524
|
-
walk2(
|
|
11757
|
+
walk2(join45(repoRoot, "modules"), "modules");
|
|
11525
11758
|
return found.sort();
|
|
11526
11759
|
}
|
|
11527
11760
|
function checkCognitoInviteTemplates(repoRoot) {
|
|
11528
11761
|
return findModuleTerraformFiles(repoRoot).flatMap(
|
|
11529
|
-
(file) => checkInviteTemplateSource(file,
|
|
11762
|
+
(file) => checkInviteTemplateSource(file, readFileSync33(join45(repoRoot, file), "utf8"))
|
|
11530
11763
|
);
|
|
11531
11764
|
}
|
|
11532
11765
|
|
|
@@ -11554,11 +11787,11 @@ async function runCognitoInviteTemplateCheck() {
|
|
|
11554
11787
|
}
|
|
11555
11788
|
|
|
11556
11789
|
// src/scripts/check-core-direct-paths.ts
|
|
11557
|
-
import { join as
|
|
11790
|
+
import { join as join47 } from "path";
|
|
11558
11791
|
|
|
11559
11792
|
// src/lib/core-direct-paths-audit.ts
|
|
11560
|
-
import { existsSync as existsSync42, readFileSync as
|
|
11561
|
-
import { join as
|
|
11793
|
+
import { existsSync as existsSync42, readFileSync as readFileSync34, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
|
|
11794
|
+
import { join as join46 } from "path";
|
|
11562
11795
|
var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
|
|
11563
11796
|
var API_ROUTE_PREFIX = "/api/v1";
|
|
11564
11797
|
var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
|
|
@@ -11605,7 +11838,7 @@ function* templateLiterals(text) {
|
|
|
11605
11838
|
i = j + 1;
|
|
11606
11839
|
}
|
|
11607
11840
|
}
|
|
11608
|
-
function
|
|
11841
|
+
function lineNumber2(text, index) {
|
|
11609
11842
|
let count = 1;
|
|
11610
11843
|
for (let i = 0; i < index && i < text.length; i++) {
|
|
11611
11844
|
if (text[i] === "\n") count += 1;
|
|
@@ -11673,7 +11906,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
|
|
|
11673
11906
|
if (touchesConcatenation(text, start - 1, end + 1)) {
|
|
11674
11907
|
found.push({
|
|
11675
11908
|
file,
|
|
11676
|
-
line:
|
|
11909
|
+
line: lineNumber2(text, start),
|
|
11677
11910
|
raw,
|
|
11678
11911
|
normalized: null,
|
|
11679
11912
|
unresolvedReason: "built with string concatenation (+) \u2014 the extracted literal is a fragment, not the whole path",
|
|
@@ -11684,7 +11917,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
|
|
|
11684
11917
|
if (isNestedTemplateLiteralArtifact(rest)) {
|
|
11685
11918
|
found.push({
|
|
11686
11919
|
file,
|
|
11687
|
-
line:
|
|
11920
|
+
line: lineNumber2(text, start),
|
|
11688
11921
|
raw,
|
|
11689
11922
|
normalized: null,
|
|
11690
11923
|
unresolvedReason: "contains a template literal nested inside its own interpolation \u2014 this extractor's scanner would otherwise truncate at the inner backtick, producing a corrupted path rather than a real mismatch",
|
|
@@ -11695,7 +11928,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
|
|
|
11695
11928
|
const normalized = stripQueryString(resolveInterpolations(rest));
|
|
11696
11929
|
found.push({
|
|
11697
11930
|
file,
|
|
11698
|
-
line:
|
|
11931
|
+
line: lineNumber2(text, start),
|
|
11699
11932
|
raw,
|
|
11700
11933
|
normalized,
|
|
11701
11934
|
unresolvedReason: null,
|
|
@@ -11717,15 +11950,15 @@ function walkFiles(root, accept, skipDir) {
|
|
|
11717
11950
|
const walk2 = (dir) => {
|
|
11718
11951
|
let entries;
|
|
11719
11952
|
try {
|
|
11720
|
-
entries =
|
|
11953
|
+
entries = readdirSync20(dir);
|
|
11721
11954
|
} catch {
|
|
11722
11955
|
return;
|
|
11723
11956
|
}
|
|
11724
11957
|
for (const entry of entries) {
|
|
11725
|
-
const p =
|
|
11958
|
+
const p = join46(dir, entry);
|
|
11726
11959
|
let st;
|
|
11727
11960
|
try {
|
|
11728
|
-
st =
|
|
11961
|
+
st = statSync12(p);
|
|
11729
11962
|
} catch {
|
|
11730
11963
|
continue;
|
|
11731
11964
|
}
|
|
@@ -11752,7 +11985,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
|
|
|
11752
11985
|
const extracted = [];
|
|
11753
11986
|
let rawTotal = 0;
|
|
11754
11987
|
for (const file of files) {
|
|
11755
|
-
const text =
|
|
11988
|
+
const text = readFileSync34(file, "utf8");
|
|
11756
11989
|
rawTotal += countRawExternalOccurrences(text, externalBases);
|
|
11757
11990
|
extracted.push(...extractCoreDirectPaths(text, file, externalBases));
|
|
11758
11991
|
}
|
|
@@ -11801,7 +12034,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
|
|
|
11801
12034
|
const prefixSet = /* @__PURE__ */ new Set();
|
|
11802
12035
|
let rawApiRouterCount = 0;
|
|
11803
12036
|
for (const file of files) {
|
|
11804
|
-
const text =
|
|
12037
|
+
const text = readFileSync34(file, "utf8");
|
|
11805
12038
|
const extraction = extractCoreRoutePrefixes(text);
|
|
11806
12039
|
rawApiRouterCount += extraction.rawApiRouterCount;
|
|
11807
12040
|
for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
|
|
@@ -11817,10 +12050,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
|
|
|
11817
12050
|
}
|
|
11818
12051
|
function resolveSiblingCoreSrc(params) {
|
|
11819
12052
|
const { estateDir, sibling } = params;
|
|
11820
|
-
const configPath =
|
|
12053
|
+
const configPath = join46(estateDir, sibling, "biffo.sibling.json");
|
|
11821
12054
|
let raw;
|
|
11822
12055
|
try {
|
|
11823
|
-
raw =
|
|
12056
|
+
raw = readFileSync34(configPath, "utf8");
|
|
11824
12057
|
} catch (err) {
|
|
11825
12058
|
throw new Error(
|
|
11826
12059
|
`cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
|
|
@@ -11840,7 +12073,7 @@ function resolveSiblingCoreSrc(params) {
|
|
|
11840
12073
|
`cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
|
|
11841
12074
|
);
|
|
11842
12075
|
}
|
|
11843
|
-
const coreApiSrcDir =
|
|
12076
|
+
const coreApiSrcDir = join46(estateDir, coreProject, "services", "api", "src");
|
|
11844
12077
|
if (!existsSync42(coreApiSrcDir)) {
|
|
11845
12078
|
throw new Error(
|
|
11846
12079
|
`cannot resolve ${sibling}'s core: biffo.sibling.json names core_project "${coreProject}", but ${coreApiSrcDir} does not exist -- the instance is missing from this estate checkout, not merely unmatched. Refusing to silently skip ${sibling} and shrink the audit's denominator.`
|
|
@@ -11884,7 +12117,7 @@ function auditSiblingCoreDirectPaths(params) {
|
|
|
11884
12117
|
async function runCoreDirectPathsCheck(opts = {}) {
|
|
11885
12118
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11886
12119
|
const sibling = opts.sibling ?? "sibling-template (self-check)";
|
|
11887
|
-
const frontendSrcDir = opts.frontendSrc ??
|
|
12120
|
+
const frontendSrcDir = opts.frontendSrc ?? join47(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
|
|
11888
12121
|
let coreApiSrcDir;
|
|
11889
12122
|
let coreProject = null;
|
|
11890
12123
|
if (opts.coreSrc) {
|
|
@@ -11900,7 +12133,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
|
|
|
11900
12133
|
coreApiSrcDir = resolution.coreApiSrcDir;
|
|
11901
12134
|
coreProject = resolution.coreProject;
|
|
11902
12135
|
} else {
|
|
11903
|
-
coreApiSrcDir =
|
|
12136
|
+
coreApiSrcDir = join47(root, "services", "api", "src");
|
|
11904
12137
|
}
|
|
11905
12138
|
const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
|
|
11906
12139
|
console.log(
|
|
@@ -11965,8 +12198,8 @@ async function runOwnershipCheck(argv) {
|
|
|
11965
12198
|
const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
11966
12199
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11967
12200
|
if (messageFile) {
|
|
11968
|
-
const { readFileSync:
|
|
11969
|
-
if (existsSync57(messageFile)) commitMessage =
|
|
12201
|
+
const { readFileSync: readFileSync47, existsSync: existsSync57 } = await import("fs");
|
|
12202
|
+
if (existsSync57(messageFile)) commitMessage = readFileSync47(messageFile, "utf8");
|
|
11970
12203
|
}
|
|
11971
12204
|
} else {
|
|
11972
12205
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -12068,20 +12301,20 @@ ${BOLD}If the divergence is deliberate${OFF}
|
|
|
12068
12301
|
|
|
12069
12302
|
// src/scripts/check-distribution-inventory.ts
|
|
12070
12303
|
import { existsSync as existsSync44 } from "fs";
|
|
12071
|
-
import { join as
|
|
12304
|
+
import { join as join49 } from "path";
|
|
12072
12305
|
|
|
12073
12306
|
// src/lib/distribution-inventory.ts
|
|
12074
|
-
import { existsSync as existsSync43, readFileSync as
|
|
12075
|
-
import { join as
|
|
12307
|
+
import { existsSync as existsSync43, readFileSync as readFileSync35 } from "fs";
|
|
12308
|
+
import { join as join48 } from "path";
|
|
12076
12309
|
var INVENTORY_FILENAME = "distribution-inventory.json";
|
|
12077
12310
|
function loadDistributionInventory(root) {
|
|
12078
|
-
const path =
|
|
12311
|
+
const path = join48(root, INVENTORY_FILENAME);
|
|
12079
12312
|
if (!existsSync43(path)) {
|
|
12080
12313
|
throw new Error(
|
|
12081
12314
|
`${INVENTORY_FILENAME} not found at ${root} -- expected it beside core-manifest.json`
|
|
12082
12315
|
);
|
|
12083
12316
|
}
|
|
12084
|
-
return JSON.parse(
|
|
12317
|
+
return JSON.parse(readFileSync35(path, "utf8"));
|
|
12085
12318
|
}
|
|
12086
12319
|
function validateInventory(inventory) {
|
|
12087
12320
|
const violations = [];
|
|
@@ -12241,7 +12474,7 @@ async function runDistributionInventoryCheck(root) {
|
|
|
12241
12474
|
);
|
|
12242
12475
|
return;
|
|
12243
12476
|
}
|
|
12244
|
-
if (!existsSync44(
|
|
12477
|
+
if (!existsSync44(join49(repoRoot, INVENTORY_FILENAME))) {
|
|
12245
12478
|
console.log(
|
|
12246
12479
|
`\u2713 distribution-inventory: skipped \u2014 no ${INVENTORY_FILENAME} at ${repoRoot}. This is a template-only registry; a checkout that legitimately has none has nothing for this guard to check.`
|
|
12247
12480
|
);
|
|
@@ -12267,7 +12500,7 @@ async function runDistributionInventoryCheck(root) {
|
|
|
12267
12500
|
}
|
|
12268
12501
|
|
|
12269
12502
|
// src/scripts/check-distribution-remote-state.ts
|
|
12270
|
-
import { join as
|
|
12503
|
+
import { join as join50 } from "path";
|
|
12271
12504
|
async function ghExecCommand(file, args) {
|
|
12272
12505
|
const result = await execa(file, args, { reject: false });
|
|
12273
12506
|
return { stdout: String(result.stdout ?? ""), exitCode: result.exitCode ?? null };
|
|
@@ -12282,7 +12515,7 @@ async function runDistributionRemoteStateCheck(root) {
|
|
|
12282
12515
|
}
|
|
12283
12516
|
}
|
|
12284
12517
|
console.log(
|
|
12285
|
-
`distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${
|
|
12518
|
+
`distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join50(repoRoot, "distribution-inventory.json")})`
|
|
12286
12519
|
);
|
|
12287
12520
|
if (assertions.length === 0) {
|
|
12288
12521
|
console.log("\u2713 distribution-remote-state: nothing declared to check");
|
|
@@ -12327,9 +12560,9 @@ ${assertion.ref}`;
|
|
|
12327
12560
|
}
|
|
12328
12561
|
|
|
12329
12562
|
// src/lib/eventbridge-log-permission-guard.ts
|
|
12330
|
-
import { readFileSync as
|
|
12331
|
-
import { join as
|
|
12332
|
-
var
|
|
12563
|
+
import { readFileSync as readFileSync36, readdirSync as readdirSync21, statSync as statSync13 } from "fs";
|
|
12564
|
+
import { join as join51 } from "path";
|
|
12565
|
+
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
|
|
12333
12566
|
var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
|
|
12334
12567
|
var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
|
|
12335
12568
|
var LOG_GROUP_ARN_REF = /aws_cloudwatch_log_group\.([\w-]+)(?:\[[^\]]*\])?\.arn/g;
|
|
@@ -12350,14 +12583,14 @@ function servicePrincipalsIn(body) {
|
|
|
12350
12583
|
function grantsPrincipal(body, principal) {
|
|
12351
12584
|
return servicePrincipalsIn(body).includes(principal);
|
|
12352
12585
|
}
|
|
12353
|
-
function
|
|
12586
|
+
function lineNumber3(text, index) {
|
|
12354
12587
|
let count = 1;
|
|
12355
12588
|
for (let i = 0; i < index && i < text.length; i++) {
|
|
12356
12589
|
if (text[i] === "\n") count += 1;
|
|
12357
12590
|
}
|
|
12358
12591
|
return count;
|
|
12359
12592
|
}
|
|
12360
|
-
function
|
|
12593
|
+
function balancedBraceSpan2(text, openIndex) {
|
|
12361
12594
|
let depth = 0;
|
|
12362
12595
|
for (let i = openIndex; i < text.length; i++) {
|
|
12363
12596
|
if (text[i] === "{") depth += 1;
|
|
@@ -12368,15 +12601,15 @@ function balancedBraceSpan(text, openIndex) {
|
|
|
12368
12601
|
}
|
|
12369
12602
|
return null;
|
|
12370
12603
|
}
|
|
12371
|
-
function
|
|
12604
|
+
function findResourceBlocks2(text, file, resourceType) {
|
|
12372
12605
|
const blocks = [];
|
|
12373
12606
|
const pattern = new RegExp(`resource\\s+"${resourceType}"\\s+"([\\w-]+)"\\s*\\{`, "g");
|
|
12374
12607
|
let m;
|
|
12375
12608
|
while ((m = pattern.exec(text)) !== null) {
|
|
12376
12609
|
const name = m[1];
|
|
12377
|
-
const line =
|
|
12610
|
+
const line = lineNumber3(text, m.index);
|
|
12378
12611
|
const openIndex = m.index + m[0].length - 1;
|
|
12379
|
-
const closeIndex =
|
|
12612
|
+
const closeIndex = balancedBraceSpan2(text, openIndex);
|
|
12380
12613
|
if (closeIndex === null) {
|
|
12381
12614
|
blocks.push({ file, line, type: resourceType, name, body: null });
|
|
12382
12615
|
continue;
|
|
@@ -12391,29 +12624,29 @@ function findResourceBlocks(text, file, resourceType) {
|
|
|
12391
12624
|
}
|
|
12392
12625
|
return blocks;
|
|
12393
12626
|
}
|
|
12394
|
-
function
|
|
12627
|
+
function countRawResourceDeclarations2(text, resourceType) {
|
|
12395
12628
|
const needle = `resource "${resourceType}"`;
|
|
12396
12629
|
return text.split(needle).length - 1;
|
|
12397
12630
|
}
|
|
12398
|
-
function
|
|
12631
|
+
function walkTerraformFiles2(root) {
|
|
12399
12632
|
const out = [];
|
|
12400
12633
|
const walk2 = (dir) => {
|
|
12401
12634
|
let entries;
|
|
12402
12635
|
try {
|
|
12403
|
-
entries =
|
|
12636
|
+
entries = readdirSync21(dir);
|
|
12404
12637
|
} catch {
|
|
12405
12638
|
return;
|
|
12406
12639
|
}
|
|
12407
12640
|
for (const entry of entries) {
|
|
12408
|
-
const p =
|
|
12641
|
+
const p = join51(dir, entry);
|
|
12409
12642
|
let st;
|
|
12410
12643
|
try {
|
|
12411
|
-
st =
|
|
12644
|
+
st = statSync13(p);
|
|
12412
12645
|
} catch {
|
|
12413
12646
|
continue;
|
|
12414
12647
|
}
|
|
12415
12648
|
if (st.isDirectory()) {
|
|
12416
|
-
if (
|
|
12649
|
+
if (SKIP_DIRS3.has(entry)) continue;
|
|
12417
12650
|
walk2(p);
|
|
12418
12651
|
continue;
|
|
12419
12652
|
}
|
|
@@ -12437,7 +12670,7 @@ function logGroupNamesReferencedIn(body) {
|
|
|
12437
12670
|
return names;
|
|
12438
12671
|
}
|
|
12439
12672
|
function auditEventBridgeLogPermissions(root) {
|
|
12440
|
-
const files =
|
|
12673
|
+
const files = walkTerraformFiles2(root);
|
|
12441
12674
|
if (files.length === 0) {
|
|
12442
12675
|
throw new Error(
|
|
12443
12676
|
`auditEventBridgeLogPermissions: no .tf files found under ${root} \u2014 the guard cannot verify anything against input it cannot see. This is a hard failure, not "0 violations": fix the path, do not treat an empty scan as a clean pass.`
|
|
@@ -12448,11 +12681,11 @@ function auditEventBridgeLogPermissions(root) {
|
|
|
12448
12681
|
let rawEventTargetCount = 0;
|
|
12449
12682
|
let rawLogPolicyCount = 0;
|
|
12450
12683
|
for (const file of files) {
|
|
12451
|
-
const text =
|
|
12452
|
-
rawEventTargetCount +=
|
|
12453
|
-
rawLogPolicyCount +=
|
|
12454
|
-
eventTargetBlocks.push(...
|
|
12455
|
-
logResourcePolicyBlocks.push(...
|
|
12684
|
+
const text = readFileSync36(file, "utf8");
|
|
12685
|
+
rawEventTargetCount += countRawResourceDeclarations2(text, EVENT_TARGET_TYPE);
|
|
12686
|
+
rawLogPolicyCount += countRawResourceDeclarations2(text, LOG_RESOURCE_POLICY_TYPE);
|
|
12687
|
+
eventTargetBlocks.push(...findResourceBlocks2(text, file, EVENT_TARGET_TYPE));
|
|
12688
|
+
logResourcePolicyBlocks.push(...findResourceBlocks2(text, file, LOG_RESOURCE_POLICY_TYPE));
|
|
12456
12689
|
}
|
|
12457
12690
|
const unterminatedBlocks = [...eventTargetBlocks, ...logResourcePolicyBlocks].filter(
|
|
12458
12691
|
(b) => b.body === null
|
|
@@ -12539,7 +12772,7 @@ async function runEventBridgeLogPermissionCheck() {
|
|
|
12539
12772
|
|
|
12540
12773
|
// src/scripts/check-instance-adoption.ts
|
|
12541
12774
|
import { existsSync as existsSync45 } from "fs";
|
|
12542
|
-
import { join as
|
|
12775
|
+
import { join as join52 } from "path";
|
|
12543
12776
|
async function runInstanceAdoptionCheck(opts = {}) {
|
|
12544
12777
|
if (!opts.instanceDir) {
|
|
12545
12778
|
console.error(
|
|
@@ -12555,7 +12788,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
|
|
|
12555
12788
|
}
|
|
12556
12789
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12557
12790
|
const theirsDir = opts.theirsDir ?? root;
|
|
12558
|
-
const instanceLabel = opts.instance ??
|
|
12791
|
+
const instanceLabel = opts.instance ?? join52(opts.instanceDir).split("/").filter(Boolean).pop();
|
|
12559
12792
|
const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
|
|
12560
12793
|
console.log(
|
|
12561
12794
|
`examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
|
|
@@ -12577,12 +12810,12 @@ async function runInstanceAdoptionCheck(opts = {}) {
|
|
|
12577
12810
|
}
|
|
12578
12811
|
|
|
12579
12812
|
// src/lib/lambda-output-guard.ts
|
|
12580
|
-
import { readFileSync as
|
|
12581
|
-
import { join as
|
|
12813
|
+
import { readFileSync as readFileSync38 } from "fs";
|
|
12814
|
+
import { join as join54 } from "path";
|
|
12582
12815
|
|
|
12583
12816
|
// src/lib/terraform-input-guard.ts
|
|
12584
|
-
import { existsSync as existsSync46, readdirSync as
|
|
12585
|
-
import { join as
|
|
12817
|
+
import { existsSync as existsSync46, readdirSync as readdirSync22, readFileSync as readFileSync37, statSync as statSync14 } from "fs";
|
|
12818
|
+
import { join as join53 } from "path";
|
|
12586
12819
|
var GUARDED_SUBCOMMANDS = [
|
|
12587
12820
|
"init",
|
|
12588
12821
|
"plan",
|
|
@@ -12596,18 +12829,18 @@ function stripComments2(source) {
|
|
|
12596
12829
|
return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
|
|
12597
12830
|
}
|
|
12598
12831
|
function vendoredPluginServiceDirs(repoRoot) {
|
|
12599
|
-
const servicesDir =
|
|
12832
|
+
const servicesDir = join53(repoRoot, "services");
|
|
12600
12833
|
const result = /* @__PURE__ */ new Set();
|
|
12601
12834
|
let entries;
|
|
12602
12835
|
try {
|
|
12603
|
-
entries =
|
|
12836
|
+
entries = readdirSync22(servicesDir);
|
|
12604
12837
|
} catch {
|
|
12605
12838
|
return result;
|
|
12606
12839
|
}
|
|
12607
12840
|
for (const entry of entries) {
|
|
12608
|
-
const full =
|
|
12609
|
-
if (!existsSync46(full) || !
|
|
12610
|
-
if (existsSync46(
|
|
12841
|
+
const full = join53(servicesDir, entry);
|
|
12842
|
+
if (!existsSync46(full) || !statSync14(full).isDirectory()) continue;
|
|
12843
|
+
if (existsSync46(join53(full, "biffo.plugin.json"))) {
|
|
12611
12844
|
result.add(entry);
|
|
12612
12845
|
}
|
|
12613
12846
|
}
|
|
@@ -12619,7 +12852,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
12619
12852
|
const walk2 = (dir, relative11) => {
|
|
12620
12853
|
let entries;
|
|
12621
12854
|
try {
|
|
12622
|
-
entries =
|
|
12855
|
+
entries = readdirSync22(dir);
|
|
12623
12856
|
} catch {
|
|
12624
12857
|
return;
|
|
12625
12858
|
}
|
|
@@ -12629,11 +12862,11 @@ function findWorkflowFiles(repoRoot) {
|
|
|
12629
12862
|
if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
|
|
12630
12863
|
continue;
|
|
12631
12864
|
}
|
|
12632
|
-
const full =
|
|
12865
|
+
const full = join53(dir, entry);
|
|
12633
12866
|
const rel = relative11 ? `${relative11}/${entry}` : entry;
|
|
12634
12867
|
let isDir;
|
|
12635
12868
|
try {
|
|
12636
|
-
isDir =
|
|
12869
|
+
isDir = statSync14(full).isDirectory();
|
|
12637
12870
|
} catch {
|
|
12638
12871
|
continue;
|
|
12639
12872
|
}
|
|
@@ -12679,7 +12912,7 @@ function checkWorkflowSource(file, rawSource) {
|
|
|
12679
12912
|
}
|
|
12680
12913
|
function checkTerraformInput(repoRoot) {
|
|
12681
12914
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
12682
|
-
(file) => checkWorkflowSource(file,
|
|
12915
|
+
(file) => checkWorkflowSource(file, readFileSync37(join53(repoRoot, file), "utf8"))
|
|
12683
12916
|
);
|
|
12684
12917
|
}
|
|
12685
12918
|
|
|
@@ -12737,7 +12970,7 @@ function checkWorkflowSource2(file, rawSource) {
|
|
|
12737
12970
|
}
|
|
12738
12971
|
function checkLambdaOutput(repoRoot) {
|
|
12739
12972
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
12740
|
-
(file) => checkWorkflowSource2(file,
|
|
12973
|
+
(file) => checkWorkflowSource2(file, readFileSync38(join54(repoRoot, file), "utf8"))
|
|
12741
12974
|
);
|
|
12742
12975
|
}
|
|
12743
12976
|
|
|
@@ -12766,7 +12999,7 @@ async function runLambdaOutputCheck() {
|
|
|
12766
12999
|
|
|
12767
13000
|
// src/scripts/check-migration-body-change.ts
|
|
12768
13001
|
import { existsSync as existsSync47 } from "fs";
|
|
12769
|
-
import { join as
|
|
13002
|
+
import { join as join55 } from "path";
|
|
12770
13003
|
|
|
12771
13004
|
// src/lib/migration-body-change-guard.ts
|
|
12772
13005
|
function checkMigrationBodyChangeMarkers(diffs) {
|
|
@@ -12832,7 +13065,7 @@ async function runMigrationBodyChangeCheck(argv) {
|
|
|
12832
13065
|
);
|
|
12833
13066
|
return;
|
|
12834
13067
|
}
|
|
12835
|
-
if (!existsSync47(
|
|
13068
|
+
if (!existsSync47(join55(root, MIGRATIONS_VERSIONS_DIR))) {
|
|
12836
13069
|
console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
|
|
12837
13070
|
return;
|
|
12838
13071
|
}
|
|
@@ -12999,8 +13232,8 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
|
|
|
12999
13232
|
}
|
|
13000
13233
|
|
|
13001
13234
|
// src/scripts/check-pipe-trap.ts
|
|
13002
|
-
import { readFileSync as
|
|
13003
|
-
import { join as
|
|
13235
|
+
import { readFileSync as readFileSync39, readdirSync as readdirSync23 } from "fs";
|
|
13236
|
+
import { join as join56, relative as relative9 } from "path";
|
|
13004
13237
|
|
|
13005
13238
|
// src/lib/pipe-trap-guard.ts
|
|
13006
13239
|
var STATUS_BEARING = [
|
|
@@ -13096,17 +13329,17 @@ function findPipeTraps(source) {
|
|
|
13096
13329
|
function shellFiles(root) {
|
|
13097
13330
|
const out = [];
|
|
13098
13331
|
for (const dir of ["scripts", ".githooks"]) {
|
|
13099
|
-
const full =
|
|
13332
|
+
const full = join56(root, dir);
|
|
13100
13333
|
let entries;
|
|
13101
13334
|
try {
|
|
13102
|
-
entries =
|
|
13335
|
+
entries = readdirSync23(full, { withFileTypes: true });
|
|
13103
13336
|
} catch {
|
|
13104
13337
|
continue;
|
|
13105
13338
|
}
|
|
13106
13339
|
for (const entry of entries) {
|
|
13107
13340
|
if (!entry.isFile()) continue;
|
|
13108
13341
|
if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
|
|
13109
|
-
out.push(
|
|
13342
|
+
out.push(join56(full, entry.name));
|
|
13110
13343
|
}
|
|
13111
13344
|
}
|
|
13112
13345
|
return out;
|
|
@@ -13122,7 +13355,7 @@ async function runPipeTrapCheck() {
|
|
|
13122
13355
|
process.exit(1);
|
|
13123
13356
|
}
|
|
13124
13357
|
const findings = files.flatMap(
|
|
13125
|
-
(file) => findPipeTraps(
|
|
13358
|
+
(file) => findPipeTraps(readFileSync39(file, "utf8")).map(
|
|
13126
13359
|
(t) => `${relative9(root, file)}:${t.line} ${t.text}
|
|
13127
13360
|
${t.reason}`
|
|
13128
13361
|
)
|
|
@@ -13139,8 +13372,8 @@ async function runPipeTrapCheck() {
|
|
|
13139
13372
|
}
|
|
13140
13373
|
|
|
13141
13374
|
// src/lib/plugin-allowlist-convention.ts
|
|
13142
|
-
import { readFileSync as
|
|
13143
|
-
import { join as
|
|
13375
|
+
import { readFileSync as readFileSync40 } from "fs";
|
|
13376
|
+
import { join as join57 } from "path";
|
|
13144
13377
|
var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
|
|
13145
13378
|
var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
|
|
13146
13379
|
var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
|
|
@@ -13151,7 +13384,7 @@ var PLUGIN = "<plugin>";
|
|
|
13151
13384
|
var ACCOUNT = "<account>";
|
|
13152
13385
|
function read(repoRoot, relative11) {
|
|
13153
13386
|
try {
|
|
13154
|
-
return
|
|
13387
|
+
return readFileSync40(join57(repoRoot, relative11), "utf8");
|
|
13155
13388
|
} catch {
|
|
13156
13389
|
throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
|
|
13157
13390
|
}
|
|
@@ -13273,32 +13506,32 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
13273
13506
|
|
|
13274
13507
|
// src/scripts/check-plugin-collisions.ts
|
|
13275
13508
|
import { existsSync as existsSync50 } from "fs";
|
|
13276
|
-
import { join as
|
|
13509
|
+
import { join as join59 } from "path";
|
|
13277
13510
|
|
|
13278
13511
|
// src/lib/plugin-collision-guard.ts
|
|
13279
|
-
import { existsSync as existsSync49, readdirSync as
|
|
13280
|
-
import { join as
|
|
13512
|
+
import { existsSync as existsSync49, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
|
|
13513
|
+
import { join as join58 } from "path";
|
|
13281
13514
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
13282
13515
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
13283
13516
|
function subdirectories(dir) {
|
|
13284
13517
|
if (!existsSync49(dir)) return [];
|
|
13285
|
-
return
|
|
13518
|
+
return readdirSync24(dir).filter((entry) => {
|
|
13286
13519
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
13287
13520
|
try {
|
|
13288
|
-
return
|
|
13521
|
+
return statSync15(join58(dir, entry)).isDirectory();
|
|
13289
13522
|
} catch {
|
|
13290
13523
|
return false;
|
|
13291
13524
|
}
|
|
13292
13525
|
});
|
|
13293
13526
|
}
|
|
13294
13527
|
function regularPackagesOf(pluginDir2) {
|
|
13295
|
-
return subdirectories(pluginDir2).filter((name) => existsSync49(
|
|
13528
|
+
return subdirectories(pluginDir2).filter((name) => existsSync49(join58(pluginDir2, name, "__init__.py"))).sort();
|
|
13296
13529
|
}
|
|
13297
13530
|
function bareTestModulesOf(pluginDir2) {
|
|
13298
|
-
const testsDir =
|
|
13531
|
+
const testsDir = join58(pluginDir2, "tests");
|
|
13299
13532
|
if (!existsSync49(testsDir)) return [];
|
|
13300
|
-
if (existsSync49(
|
|
13301
|
-
return
|
|
13533
|
+
if (existsSync49(join58(testsDir, "__init__.py"))) return [];
|
|
13534
|
+
return readdirSync24(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
13302
13535
|
}
|
|
13303
13536
|
function findCollisions(servicesDir, pluginDirs) {
|
|
13304
13537
|
const plugins = (pluginDirs ?? subdirectories(servicesDir)).filter((name) => !name.startsWith("_")).filter((name) => name !== "api").sort();
|
|
@@ -13306,7 +13539,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
13306
13539
|
const gather = (kind, namesOf) => {
|
|
13307
13540
|
const claims = /* @__PURE__ */ new Map();
|
|
13308
13541
|
for (const plugin of plugins) {
|
|
13309
|
-
for (const name of namesOf(
|
|
13542
|
+
for (const name of namesOf(join58(servicesDir, plugin))) {
|
|
13310
13543
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
13311
13544
|
}
|
|
13312
13545
|
}
|
|
@@ -13344,7 +13577,7 @@ function formatCollisions(collisions) {
|
|
|
13344
13577
|
// src/scripts/check-plugin-collisions.ts
|
|
13345
13578
|
async function runPluginCollisionCheck() {
|
|
13346
13579
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13347
|
-
const servicesDir =
|
|
13580
|
+
const servicesDir = join59(root, "services");
|
|
13348
13581
|
if (!existsSync50(servicesDir)) {
|
|
13349
13582
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
13350
13583
|
return;
|
|
@@ -13362,25 +13595,25 @@ async function runPluginCollisionCheck() {
|
|
|
13362
13595
|
}
|
|
13363
13596
|
|
|
13364
13597
|
// src/lib/plugin-terraform-guard.ts
|
|
13365
|
-
import { existsSync as existsSync51, readFileSync as
|
|
13366
|
-
import { dirname as dirname10, join as
|
|
13367
|
-
var
|
|
13598
|
+
import { existsSync as existsSync51, readFileSync as readFileSync41, readdirSync as readdirSync25 } from "fs";
|
|
13599
|
+
import { dirname as dirname10, join as join60, relative as relative10, sep as sep4 } from "path";
|
|
13600
|
+
var SKIP_DIRS4 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
13368
13601
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
13369
13602
|
function findPluginManifests(root) {
|
|
13370
13603
|
const found = [];
|
|
13371
13604
|
const walk2 = (dir) => {
|
|
13372
13605
|
let entries;
|
|
13373
13606
|
try {
|
|
13374
|
-
entries =
|
|
13607
|
+
entries = readdirSync25(dir, { withFileTypes: true });
|
|
13375
13608
|
} catch {
|
|
13376
13609
|
return;
|
|
13377
13610
|
}
|
|
13378
13611
|
for (const entry of entries) {
|
|
13379
13612
|
if (entry.isDirectory()) {
|
|
13380
|
-
if (
|
|
13381
|
-
walk2(
|
|
13613
|
+
if (SKIP_DIRS4.has(entry.name)) continue;
|
|
13614
|
+
walk2(join60(dir, entry.name));
|
|
13382
13615
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
13383
|
-
found.push(relative10(root,
|
|
13616
|
+
found.push(relative10(root, join60(dir, entry.name)).split(sep4).join("/"));
|
|
13384
13617
|
}
|
|
13385
13618
|
}
|
|
13386
13619
|
};
|
|
@@ -13390,7 +13623,7 @@ function findPluginManifests(root) {
|
|
|
13390
13623
|
function readSubscriptions(absManifestPath) {
|
|
13391
13624
|
let parsed;
|
|
13392
13625
|
try {
|
|
13393
|
-
parsed = JSON.parse(
|
|
13626
|
+
parsed = JSON.parse(readFileSync41(absManifestPath, "utf8"));
|
|
13394
13627
|
} catch {
|
|
13395
13628
|
return null;
|
|
13396
13629
|
}
|
|
@@ -13405,14 +13638,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
13405
13638
|
}
|
|
13406
13639
|
function checkPluginTerraform(root) {
|
|
13407
13640
|
const violations = [];
|
|
13408
|
-
const coreManifest = existsSync51(
|
|
13641
|
+
const coreManifest = existsSync51(join60(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
13409
13642
|
for (const manifest of findPluginManifests(root)) {
|
|
13410
13643
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
13411
|
-
const absManifest =
|
|
13644
|
+
const absManifest = join60(root, manifest);
|
|
13412
13645
|
const subscriptions = readSubscriptions(absManifest);
|
|
13413
13646
|
if (subscriptions === null) continue;
|
|
13414
13647
|
const pluginDir2 = dirname10(absManifest);
|
|
13415
|
-
if (existsSync51(
|
|
13648
|
+
if (existsSync51(join60(pluginDir2, "terraform"))) continue;
|
|
13416
13649
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
13417
13650
|
violations.push({
|
|
13418
13651
|
manifest,
|
|
@@ -13444,11 +13677,11 @@ async function runPluginTerraformCheck() {
|
|
|
13444
13677
|
|
|
13445
13678
|
// src/scripts/check-plugin-tool-supply.ts
|
|
13446
13679
|
import { existsSync as existsSync53 } from "fs";
|
|
13447
|
-
import { join as
|
|
13680
|
+
import { join as join62 } from "path";
|
|
13448
13681
|
|
|
13449
13682
|
// src/lib/plugin-tool-supply-audit.ts
|
|
13450
|
-
import { existsSync as existsSync52, readFileSync as
|
|
13451
|
-
import { join as
|
|
13683
|
+
import { existsSync as existsSync52, readFileSync as readFileSync42, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
|
|
13684
|
+
import { join as join61 } from "path";
|
|
13452
13685
|
|
|
13453
13686
|
// src/lib/openrouter-model-snapshot.ts
|
|
13454
13687
|
var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
|
|
@@ -13859,13 +14092,13 @@ var OPENROUTER_MODEL_IDS = [
|
|
|
13859
14092
|
function listDirs(root) {
|
|
13860
14093
|
let entries;
|
|
13861
14094
|
try {
|
|
13862
|
-
entries =
|
|
14095
|
+
entries = readdirSync26(root);
|
|
13863
14096
|
} catch {
|
|
13864
14097
|
return [];
|
|
13865
14098
|
}
|
|
13866
14099
|
return entries.filter((e) => {
|
|
13867
14100
|
try {
|
|
13868
|
-
return
|
|
14101
|
+
return statSync16(join61(root, e)).isDirectory();
|
|
13869
14102
|
} catch {
|
|
13870
14103
|
return false;
|
|
13871
14104
|
}
|
|
@@ -13876,15 +14109,15 @@ function walkFiles2(root, accept, skipDir) {
|
|
|
13876
14109
|
const walk2 = (dir) => {
|
|
13877
14110
|
let entries;
|
|
13878
14111
|
try {
|
|
13879
|
-
entries =
|
|
14112
|
+
entries = readdirSync26(dir);
|
|
13880
14113
|
} catch {
|
|
13881
14114
|
return;
|
|
13882
14115
|
}
|
|
13883
14116
|
for (const entry of entries) {
|
|
13884
|
-
const p =
|
|
14117
|
+
const p = join61(dir, entry);
|
|
13885
14118
|
let st;
|
|
13886
14119
|
try {
|
|
13887
|
-
st =
|
|
14120
|
+
st = statSync16(p);
|
|
13888
14121
|
} catch {
|
|
13889
14122
|
continue;
|
|
13890
14123
|
}
|
|
@@ -13907,14 +14140,14 @@ function pluginPythonFiles(pluginDir2) {
|
|
|
13907
14140
|
);
|
|
13908
14141
|
}
|
|
13909
14142
|
function pluginTerraformFiles(pluginDir2) {
|
|
13910
|
-
const tfDir =
|
|
14143
|
+
const tfDir = join61(pluginDir2, "terraform");
|
|
13911
14144
|
let entries;
|
|
13912
14145
|
try {
|
|
13913
|
-
entries =
|
|
14146
|
+
entries = readdirSync26(tfDir);
|
|
13914
14147
|
} catch {
|
|
13915
14148
|
return [];
|
|
13916
14149
|
}
|
|
13917
|
-
return entries.filter((e) => e.endsWith(".tf")).map((e) =>
|
|
14150
|
+
return entries.filter((e) => e.endsWith(".tf")).map((e) => join61(tfDir, e)).sort();
|
|
13918
14151
|
}
|
|
13919
14152
|
function extractManifestTools(manifestText) {
|
|
13920
14153
|
let parsed;
|
|
@@ -14166,8 +14399,8 @@ function isSnapshotStale(fetchedAt, now) {
|
|
|
14166
14399
|
function normalizeModelId(id) {
|
|
14167
14400
|
return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
|
|
14168
14401
|
}
|
|
14169
|
-
var CONFIG_PY_PATH =
|
|
14170
|
-
var ORCHESTRATION_SCHEMA_PATH =
|
|
14402
|
+
var CONFIG_PY_PATH = join61("services", "api", "src", "api", "config.py");
|
|
14403
|
+
var ORCHESTRATION_SCHEMA_PATH = join61(
|
|
14171
14404
|
"services",
|
|
14172
14405
|
"api",
|
|
14173
14406
|
"src",
|
|
@@ -14179,8 +14412,8 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14179
14412
|
const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
|
|
14180
14413
|
const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
|
|
14181
14414
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
14182
|
-
const configPath =
|
|
14183
|
-
const orchestrationPath =
|
|
14415
|
+
const configPath = join61(repoRoot, CONFIG_PY_PATH);
|
|
14416
|
+
const orchestrationPath = join61(repoRoot, ORCHESTRATION_SCHEMA_PATH);
|
|
14184
14417
|
const configMissing = !existsSync52(configPath);
|
|
14185
14418
|
const orchestrationSchemaMissing = !existsSync52(orchestrationPath);
|
|
14186
14419
|
const knownSet = new Set(knownModelIds);
|
|
@@ -14200,13 +14433,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14200
14433
|
};
|
|
14201
14434
|
let settingsBlind = false;
|
|
14202
14435
|
if (!configMissing) {
|
|
14203
|
-
const settingsFields = extractSettingsModelFields(
|
|
14436
|
+
const settingsFields = extractSettingsModelFields(readFileSync42(configPath, "utf8"));
|
|
14204
14437
|
if (settingsFields.length === 0) settingsBlind = true;
|
|
14205
14438
|
for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
|
|
14206
14439
|
}
|
|
14207
14440
|
let curatedFieldsBlind = false;
|
|
14208
14441
|
if (!orchestrationSchemaMissing) {
|
|
14209
|
-
const curated = extractCuratedModelFields(
|
|
14442
|
+
const curated = extractCuratedModelFields(readFileSync42(orchestrationPath, "utf8"));
|
|
14210
14443
|
if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
|
|
14211
14444
|
curatedFieldsBlind = true;
|
|
14212
14445
|
}
|
|
@@ -14253,7 +14486,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14253
14486
|
function discoverPluginDirs(pluginsRoot) {
|
|
14254
14487
|
return listDirs(pluginsRoot).filter((name) => {
|
|
14255
14488
|
try {
|
|
14256
|
-
return
|
|
14489
|
+
return statSync16(join61(pluginsRoot, name, "biffo.plugin.json")).isFile();
|
|
14257
14490
|
} catch {
|
|
14258
14491
|
return false;
|
|
14259
14492
|
}
|
|
@@ -14266,8 +14499,8 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14266
14499
|
let terraformBlind = false;
|
|
14267
14500
|
let totalDeclaredTools = 0;
|
|
14268
14501
|
for (const name of pluginNames) {
|
|
14269
|
-
const pluginDir2 =
|
|
14270
|
-
const manifestText =
|
|
14502
|
+
const pluginDir2 = join61(pluginsRoot, name);
|
|
14503
|
+
const manifestText = readFileSync42(join61(pluginDir2, "biffo.plugin.json"), "utf8");
|
|
14271
14504
|
const manifest = extractManifestTools(manifestText);
|
|
14272
14505
|
if (manifest.parseError) {
|
|
14273
14506
|
findings.push({
|
|
@@ -14285,13 +14518,13 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14285
14518
|
totalDeclaredTools += manifest.tools.length;
|
|
14286
14519
|
const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
|
|
14287
14520
|
file: f,
|
|
14288
|
-
text:
|
|
14521
|
+
text: readFileSync42(f, "utf8")
|
|
14289
14522
|
}));
|
|
14290
14523
|
const resolver = buildSymbolResolver(pySources);
|
|
14291
14524
|
const registry = extractToolRegistryEntries(pySources, resolver);
|
|
14292
14525
|
if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
|
|
14293
14526
|
const tfFiles = pluginTerraformFiles(pluginDir2);
|
|
14294
|
-
const tfText = tfFiles.map((f) =>
|
|
14527
|
+
const tfText = tfFiles.map((f) => readFileSync42(f, "utf8")).join("\n");
|
|
14295
14528
|
const terraform = extractTerraformEnvKeys(tfText);
|
|
14296
14529
|
if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
|
|
14297
14530
|
for (const toolName of manifest.tools) {
|
|
@@ -14365,7 +14598,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14365
14598
|
requiredEnvVars: envResult.envVars,
|
|
14366
14599
|
missingEnvVars: anyWired ? [] : envResult.envVars,
|
|
14367
14600
|
status: anyWired ? "ok" : "missing-env",
|
|
14368
|
-
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${
|
|
14601
|
+
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join61(pluginDir2, "terraform")}, so this deployment can never supply it`
|
|
14369
14602
|
});
|
|
14370
14603
|
}
|
|
14371
14604
|
}
|
|
@@ -14398,7 +14631,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14398
14631
|
async function runPluginToolSupplyCheck() {
|
|
14399
14632
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
14400
14633
|
let allOk = true;
|
|
14401
|
-
const pluginsRoot =
|
|
14634
|
+
const pluginsRoot = join62(root, "services", "_plugins");
|
|
14402
14635
|
if (!existsSync53(pluginsRoot)) {
|
|
14403
14636
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
14404
14637
|
} else {
|
|
@@ -14429,7 +14662,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
14429
14662
|
console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
|
|
14430
14663
|
}
|
|
14431
14664
|
}
|
|
14432
|
-
const servicesApiRoot =
|
|
14665
|
+
const servicesApiRoot = join62(root, "services", "api");
|
|
14433
14666
|
if (!existsSync53(servicesApiRoot)) {
|
|
14434
14667
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
14435
14668
|
} else {
|
|
@@ -14604,10 +14837,10 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
14604
14837
|
}
|
|
14605
14838
|
|
|
14606
14839
|
// src/scripts/check-shared-file-reduction.ts
|
|
14607
|
-
import { readFileSync as
|
|
14840
|
+
import { readFileSync as readFileSync44 } from "fs";
|
|
14608
14841
|
|
|
14609
14842
|
// src/lib/shared-file-reduction-guard.ts
|
|
14610
|
-
import { readFileSync as
|
|
14843
|
+
import { readFileSync as readFileSync43 } from "fs";
|
|
14611
14844
|
var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
|
|
14612
14845
|
var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
|
|
14613
14846
|
var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
@@ -14734,7 +14967,7 @@ function formatReductionReport(report) {
|
|
|
14734
14967
|
// src/scripts/check-shared-file-reduction.ts
|
|
14735
14968
|
function readStdin() {
|
|
14736
14969
|
try {
|
|
14737
|
-
return
|
|
14970
|
+
return readFileSync44(0, "utf8");
|
|
14738
14971
|
} catch {
|
|
14739
14972
|
return "";
|
|
14740
14973
|
}
|
|
@@ -14751,15 +14984,15 @@ function pairsFromTsv(tsv) {
|
|
|
14751
14984
|
const [target, existingPath, incomingPath] = fields;
|
|
14752
14985
|
pairs.push({
|
|
14753
14986
|
target,
|
|
14754
|
-
existing:
|
|
14755
|
-
incoming:
|
|
14987
|
+
existing: readFileSync44(existingPath, "utf8"),
|
|
14988
|
+
incoming: readFileSync44(incomingPath, "utf8")
|
|
14756
14989
|
});
|
|
14757
14990
|
}
|
|
14758
14991
|
return pairs;
|
|
14759
14992
|
}
|
|
14760
14993
|
function loadAccepted(manifestPath) {
|
|
14761
14994
|
if (!manifestPath) return {};
|
|
14762
|
-
const parsed = JSON.parse(
|
|
14995
|
+
const parsed = JSON.parse(readFileSync44(manifestPath, "utf8"));
|
|
14763
14996
|
return parsed.acceptedReductions ?? {};
|
|
14764
14997
|
}
|
|
14765
14998
|
async function runSharedFileReductionCheck(args) {
|
|
@@ -14767,13 +15000,13 @@ async function runSharedFileReductionCheck(args) {
|
|
|
14767
15000
|
let accepted;
|
|
14768
15001
|
try {
|
|
14769
15002
|
if (args.pairs) {
|
|
14770
|
-
pairs = pairsFromTsv(args.pairs === "-" ? readStdin() :
|
|
15003
|
+
pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync44(args.pairs, "utf8"));
|
|
14771
15004
|
} else if (args.target && args.existing && args.incoming) {
|
|
14772
15005
|
pairs = [
|
|
14773
15006
|
{
|
|
14774
15007
|
target: args.target,
|
|
14775
|
-
existing:
|
|
14776
|
-
incoming:
|
|
15008
|
+
existing: readFileSync44(args.existing, "utf8"),
|
|
15009
|
+
incoming: readFileSync44(args.incoming, "utf8")
|
|
14777
15010
|
}
|
|
14778
15011
|
];
|
|
14779
15012
|
} else {
|
|
@@ -14807,12 +15040,12 @@ async function runSharedFileReductionCheck(args) {
|
|
|
14807
15040
|
}
|
|
14808
15041
|
|
|
14809
15042
|
// src/scripts/check-skeleton-drift.ts
|
|
14810
|
-
import { existsSync as existsSync54, readdirSync as
|
|
14811
|
-
import { join as
|
|
15043
|
+
import { existsSync as existsSync54, readdirSync as readdirSync28 } from "fs";
|
|
15044
|
+
import { join as join64 } from "path";
|
|
14812
15045
|
|
|
14813
15046
|
// src/lib/skeleton-drift-guard.ts
|
|
14814
|
-
import { readFileSync as
|
|
14815
|
-
import { join as
|
|
15047
|
+
import { readFileSync as readFileSync45, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
|
|
15048
|
+
import { join as join63 } from "path";
|
|
14816
15049
|
var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
|
|
14817
15050
|
var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
|
|
14818
15051
|
var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
|
|
@@ -14870,16 +15103,16 @@ function walk(dir, base = dir) {
|
|
|
14870
15103
|
const out = [];
|
|
14871
15104
|
let entries;
|
|
14872
15105
|
try {
|
|
14873
|
-
entries =
|
|
15106
|
+
entries = readdirSync27(dir);
|
|
14874
15107
|
} catch {
|
|
14875
15108
|
return out;
|
|
14876
15109
|
}
|
|
14877
15110
|
for (const entry of entries) {
|
|
14878
15111
|
if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
|
|
14879
|
-
const abs =
|
|
15112
|
+
const abs = join63(dir, entry);
|
|
14880
15113
|
let isDir;
|
|
14881
15114
|
try {
|
|
14882
|
-
isDir =
|
|
15115
|
+
isDir = statSync17(abs).isDirectory();
|
|
14883
15116
|
} catch {
|
|
14884
15117
|
continue;
|
|
14885
15118
|
}
|
|
@@ -14898,7 +15131,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
|
|
|
14898
15131
|
if (!rule.appliesTo(rel)) continue;
|
|
14899
15132
|
let contents;
|
|
14900
15133
|
try {
|
|
14901
|
-
contents =
|
|
15134
|
+
contents = readFileSync45(join63(skeletonRoot, rel), "utf8");
|
|
14902
15135
|
} catch {
|
|
14903
15136
|
continue;
|
|
14904
15137
|
}
|
|
@@ -14927,23 +15160,23 @@ function formatViolations2(violations) {
|
|
|
14927
15160
|
|
|
14928
15161
|
// src/scripts/check-skeleton-drift.ts
|
|
14929
15162
|
function discoverSkeletons(root) {
|
|
14930
|
-
const skeletonsDir =
|
|
15163
|
+
const skeletonsDir = join64(root, "_skeletons");
|
|
14931
15164
|
let entries;
|
|
14932
15165
|
try {
|
|
14933
|
-
entries =
|
|
15166
|
+
entries = readdirSync28(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
14934
15167
|
} catch {
|
|
14935
15168
|
return [];
|
|
14936
15169
|
}
|
|
14937
|
-
return entries.filter((name) => existsSync54(
|
|
15170
|
+
return entries.filter((name) => existsSync54(join64(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
14938
15171
|
}
|
|
14939
15172
|
async function runSkeletonDriftCheck() {
|
|
14940
15173
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
14941
15174
|
const skeletons = discoverSkeletons(root);
|
|
14942
15175
|
let filesConsidered = 0;
|
|
14943
15176
|
for (const name of skeletons) {
|
|
14944
|
-
const skeletonRoot =
|
|
15177
|
+
const skeletonRoot = join64(root, "_skeletons", name);
|
|
14945
15178
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
14946
|
-
if (existsSync54(
|
|
15179
|
+
if (existsSync54(join64(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
14947
15180
|
filesConsidered += 1;
|
|
14948
15181
|
}
|
|
14949
15182
|
}
|
|
@@ -14957,7 +15190,7 @@ async function runSkeletonDriftCheck() {
|
|
|
14957
15190
|
process.exit(1);
|
|
14958
15191
|
}
|
|
14959
15192
|
const violations = skeletons.flatMap(
|
|
14960
|
-
(name) => auditSkeleton(
|
|
15193
|
+
(name) => auditSkeleton(join64(root, "_skeletons", name), name)
|
|
14961
15194
|
);
|
|
14962
15195
|
if (violations.length > 0) {
|
|
14963
15196
|
console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
|
|
@@ -14993,7 +15226,7 @@ async function runTerraformInputCheck() {
|
|
|
14993
15226
|
|
|
14994
15227
|
// src/commands/check.ts
|
|
14995
15228
|
var checkCommand = new Command24("check").description(
|
|
14996
|
-
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
15229
|
+
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state, api-gateway-integration) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
14997
15230
|
);
|
|
14998
15231
|
checkCommand.command("ownership").description("Refuse changes to template-owned paths in an instance (#370)").argument("[base]", "Base branch to diff against; defaults to $GITHUB_BASE_REF").option("--staged <messageFile>", "Check staged changes instead of a branch diff (commit hook)").allowExcessArguments(true).action(async () => {
|
|
14999
15232
|
await runOwnershipCheck(rawArgsAfter("ownership"));
|
|
@@ -15117,6 +15350,11 @@ checkCommand.command("terraform-input").description(
|
|
|
15117
15350
|
).action(async () => {
|
|
15118
15351
|
await runTerraformInputCheck();
|
|
15119
15352
|
});
|
|
15353
|
+
checkCommand.command("api-gateway-integration").description(
|
|
15354
|
+
"Refuse an env-owned aws_apigatewayv2_integration targeting a Lambda's raw, unqualified function_arn when modules/cloud/aws/api-gateway already fronts that same Lambda with an alias-qualified aws_lambda_permission (#1747) \u2014 terraform validate/plan pass on both sides in isolation, so this is the only signal before a real deploy 500s (biffo-template#1900; tabsii-platform took 11 public routes down this exact way)."
|
|
15355
|
+
).action(async () => {
|
|
15356
|
+
await runApiGatewayIntegrationCheck();
|
|
15357
|
+
});
|
|
15120
15358
|
checkCommand.command("shared-file-reduction").description(
|
|
15121
15359
|
"Refuse a shared-files.json overwrite that would DELETE tests from the satellite it lands in (#1577) \u2014 filesIfPresent is a one-way cp and nothing ever compared the two copies. Invoked by scripts/shared-sync.sh at the moment of the write, not by per-PR CI: both sides of the comparison only exist together inside a sync run. Compares TEST TITLES in TS/JS test files and nothing else \u2014 see the guard module for what it does not catch."
|
|
15122
15360
|
).option("--pairs <tsv>", "TSV of target<TAB>existing<TAB>incoming lines, or - for stdin").option("--target <path>", "Single pair: path inside the satellite").option("--existing <path>", "Single pair: the satellite's current copy").option("--incoming <path>", "Single pair: the canonical copy that would replace it").option("--manifest <path>", "shared-files.json, read for acceptedReductions").action(
|
|
@@ -15148,8 +15386,8 @@ function rawArgsAfter(subcommand) {
|
|
|
15148
15386
|
}
|
|
15149
15387
|
|
|
15150
15388
|
// src/commands/doctor.ts
|
|
15151
|
-
import { existsSync as existsSync55, readFileSync as
|
|
15152
|
-
import { join as
|
|
15389
|
+
import { existsSync as existsSync55, readFileSync as readFileSync46 } from "fs";
|
|
15390
|
+
import { join as join65, resolve as resolve20 } from "path";
|
|
15153
15391
|
import chalk21 from "chalk";
|
|
15154
15392
|
import { Command as Command25 } from "commander";
|
|
15155
15393
|
|
|
@@ -15544,10 +15782,10 @@ async function runDoctorFixBranches(cwd, facts, deps = { git: new GitAdapter(),
|
|
|
15544
15782
|
return reapAllBareBranches(cwd, facts.branches, facts.worktrees, facts.currentBranch, deps);
|
|
15545
15783
|
}
|
|
15546
15784
|
function readLocalCoreVersion(cwd) {
|
|
15547
|
-
const path =
|
|
15785
|
+
const path = join65(cwd, INSTANCE_CORE_FILE);
|
|
15548
15786
|
if (!existsSync55(path)) return null;
|
|
15549
15787
|
try {
|
|
15550
|
-
return extractVersionField(
|
|
15788
|
+
return extractVersionField(readFileSync46(path, "utf8"));
|
|
15551
15789
|
} catch {
|
|
15552
15790
|
return null;
|
|
15553
15791
|
}
|
|
@@ -15567,10 +15805,10 @@ function extractVersionField(contents) {
|
|
|
15567
15805
|
return match?.[1] ?? null;
|
|
15568
15806
|
}
|
|
15569
15807
|
function readFossil(cwd) {
|
|
15570
|
-
const path =
|
|
15808
|
+
const path = join65(cwd, CORE_VERSION_FILE);
|
|
15571
15809
|
if (!existsSync55(path)) return null;
|
|
15572
15810
|
try {
|
|
15573
|
-
const value =
|
|
15811
|
+
const value = readFileSync46(path, "utf8").trim();
|
|
15574
15812
|
return value === "" ? null : value;
|
|
15575
15813
|
} catch {
|
|
15576
15814
|
return null;
|
|
@@ -16093,18 +16331,18 @@ function resolveGithubToken4() {
|
|
|
16093
16331
|
|
|
16094
16332
|
// src/lib/packaged-script-command.ts
|
|
16095
16333
|
import { spawnSync } from "child_process";
|
|
16096
|
-
import { chmodSync as chmodSync2, statSync as
|
|
16334
|
+
import { chmodSync as chmodSync2, statSync as statSync18 } from "fs";
|
|
16097
16335
|
import { dirname as dirname12 } from "path";
|
|
16098
16336
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
16099
16337
|
import { Command as Command27 } from "commander";
|
|
16100
16338
|
|
|
16101
16339
|
// src/lib/packaged-scripts.ts
|
|
16102
16340
|
import { existsSync as existsSync56 } from "fs";
|
|
16103
|
-
import { dirname as dirname11, join as
|
|
16341
|
+
import { dirname as dirname11, join as join66 } from "path";
|
|
16104
16342
|
function findPackagedScript(startDir, relativePath) {
|
|
16105
16343
|
let dir = startDir;
|
|
16106
16344
|
for (; ; ) {
|
|
16107
|
-
const candidate =
|
|
16345
|
+
const candidate = join66(dir, relativePath);
|
|
16108
16346
|
if (existsSync56(candidate)) return candidate;
|
|
16109
16347
|
const parent = dirname11(dir);
|
|
16110
16348
|
if (parent === dir) return null;
|
|
@@ -16119,7 +16357,7 @@ It ships with this package via cli/scripts/packaged-root-assets.mjs; if you are
|
|
|
16119
16357
|
// src/lib/packaged-script-command.ts
|
|
16120
16358
|
function ensureExecutable(script) {
|
|
16121
16359
|
try {
|
|
16122
|
-
const { mode } =
|
|
16360
|
+
const { mode } = statSync18(script);
|
|
16123
16361
|
if ((mode & 73) === 0) chmodSync2(script, mode | 493);
|
|
16124
16362
|
} catch {
|
|
16125
16363
|
}
|