paqad-ai 0.1.2 → 0.1.3
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/cli/index.js +471 -122
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +486 -151
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/capabilities/coding/stacks/flutter/pack.yaml +5 -1
- package/runtime/capabilities/coding/stacks/laravel/pack.yaml +15 -1
- package/runtime/capabilities/coding/stacks/react/pack.yaml +6 -2
- package/runtime/capabilities/coding/stacks/vue/pack.yaml +6 -2
- package/runtime/capabilities/security/rules/pentest.md +109 -9
- package/runtime/capabilities/security/skills/auth-mechanism-review/SKILL.md +88 -0
- package/runtime/capabilities/security/skills/auth-mechanism-review/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/auth-mechanism-review/references/auth-attack-checklist.md +84 -0
- package/runtime/capabilities/security/skills/business-logic-abuse-review/references/abuse-cases.md +33 -5
- package/runtime/capabilities/security/skills/cryptographic-review/SKILL.md +72 -0
- package/runtime/capabilities/security/skills/cryptographic-review/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/cryptographic-review/references/crypto-weakness-patterns.md +156 -0
- package/runtime/capabilities/security/skills/dependency-advisory-triage/references/advisory-normalization.md +33 -1
- package/runtime/capabilities/security/skills/input-validation-review/SKILL.md +75 -0
- package/runtime/capabilities/security/skills/input-validation-review/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/input-validation-review/references/input-attack-patterns.md +74 -0
- package/runtime/capabilities/security/skills/logging-monitoring-review/SKILL.md +72 -0
- package/runtime/capabilities/security/skills/logging-monitoring-review/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/logging-monitoring-review/references/logging-gaps-checklist.md +77 -0
- package/runtime/capabilities/security/skills/permission-boundary-review/references/boundary-checklist.md +31 -1
- package/runtime/capabilities/security/skills/rate-limiting-review/SKILL.md +67 -0
- package/runtime/capabilities/security/skills/rate-limiting-review/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/rate-limiting-review/references/rate-limit-signals.md +153 -0
- package/runtime/capabilities/security/skills/runtime-surface-probing/references/runtime-surface-checks.md +57 -3
- package/runtime/capabilities/security/skills/stride-threat-model/SKILL.md +60 -0
- package/runtime/capabilities/security/skills/stride-threat-model/agents/openai.yaml +3 -0
- package/runtime/capabilities/security/skills/stride-threat-model/references/stride-checklist.md +69 -0
- package/runtime/hooks/silent-update.sh +115 -0
- package/runtime/templates/agent-configs/agents.md.hbs +8 -0
- package/runtime/templates/agent-configs/claude.md.hbs +8 -0
- package/runtime/templates/agent-configs/gemini.md.hbs +8 -0
- package/runtime/templates/agent-configs/junie.md.hbs +8 -0
- package/scripts/deprecate-old-versions.sh +55 -0
package/dist/cli/index.js
CHANGED
|
@@ -96,7 +96,12 @@ var PATHS = {
|
|
|
96
96
|
CLAUDE_MD: "CLAUDE.md",
|
|
97
97
|
AGENTS_MD: "AGENTS.md",
|
|
98
98
|
GEMINI_MD: "GEMINI.md",
|
|
99
|
-
SCRIPTS_DIR: "scripts"
|
|
99
|
+
SCRIPTS_DIR: "scripts",
|
|
100
|
+
HOOKS_DIR: ".paqad/hooks",
|
|
101
|
+
LOGS_DIR: ".paqad/logs",
|
|
102
|
+
LOCKS_DIR: ".paqad/locks",
|
|
103
|
+
AUTO_UPDATE_LOG: ".paqad/logs/auto-update.log",
|
|
104
|
+
HOOKS_SILENT_UPDATE: ".paqad/hooks/silent-update.sh"
|
|
100
105
|
};
|
|
101
106
|
var REGISTRIES = [
|
|
102
107
|
"module-registry.md",
|
|
@@ -4137,8 +4142,10 @@ function writeFrameworkMetadata(projectRoot, version) {
|
|
|
4137
4142
|
mkdirSync2(dirname11(join21(projectRoot, PATHS.FRAMEWORK_VERSION)), {
|
|
4138
4143
|
recursive: true
|
|
4139
4144
|
});
|
|
4140
|
-
|
|
4141
|
-
|
|
4145
|
+
const content = `version=${version}
|
|
4146
|
+
updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
4147
|
+
`;
|
|
4148
|
+
writeFileSync2(join21(projectRoot, PATHS.FRAMEWORK_VERSION), content);
|
|
4142
4149
|
writeFileSync2(join21(projectRoot, PATHS.FRAMEWORK_PATH), `${resolveFrameworkInstallPath()}
|
|
4143
4150
|
`);
|
|
4144
4151
|
}
|
|
@@ -5518,6 +5525,10 @@ function writeGeneratedFiles(projectRoot, files) {
|
|
|
5518
5525
|
return { written, skipped };
|
|
5519
5526
|
}
|
|
5520
5527
|
|
|
5528
|
+
// src/onboarding/orchestrator.ts
|
|
5529
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
5530
|
+
import { join as join33 } from "path";
|
|
5531
|
+
|
|
5521
5532
|
// src/resolver/resolver.ts
|
|
5522
5533
|
import fg2 from "fast-glob";
|
|
5523
5534
|
import { basename as basename3, extname, relative as relative3 } from "pathe";
|
|
@@ -6393,6 +6404,17 @@ var OnboardingOrchestrator = class {
|
|
|
6393
6404
|
stack_profile: selections.stack_profile
|
|
6394
6405
|
})
|
|
6395
6406
|
);
|
|
6407
|
+
const silentUpdateSrc = join33(runtimeRoot, "..", "hooks", "silent-update.sh");
|
|
6408
|
+
try {
|
|
6409
|
+
const hookContent = readFileSync12(silentUpdateSrc, "utf8");
|
|
6410
|
+
generatedFiles.push({
|
|
6411
|
+
path: PATHS.HOOKS_SILENT_UPDATE,
|
|
6412
|
+
content: hookContent,
|
|
6413
|
+
autoUpdate: true,
|
|
6414
|
+
executable: true
|
|
6415
|
+
});
|
|
6416
|
+
} catch {
|
|
6417
|
+
}
|
|
6396
6418
|
const writeResult = writeGeneratedFiles(options.projectRoot, generatedFiles);
|
|
6397
6419
|
const drift = await writeStackArtifacts(
|
|
6398
6420
|
options.projectRoot,
|
|
@@ -6403,6 +6425,7 @@ var OnboardingOrchestrator = class {
|
|
|
6403
6425
|
writeProjectProfile2(options.projectRoot, profile);
|
|
6404
6426
|
writeGitignore(options.projectRoot);
|
|
6405
6427
|
writeDetectionReport(options.projectRoot, detection);
|
|
6428
|
+
writeFrameworkMetadata(options.projectRoot, VERSION);
|
|
6406
6429
|
bootstrapFramework(options.projectRoot);
|
|
6407
6430
|
const manifestPath = writeOnboardingManifest(options.projectRoot, {
|
|
6408
6431
|
framework_version: VERSION,
|
|
@@ -6676,7 +6699,7 @@ function buildRustCommands(usingCompose) {
|
|
|
6676
6699
|
}
|
|
6677
6700
|
|
|
6678
6701
|
// src/onboarding/scaffold-generator.ts
|
|
6679
|
-
import { join as
|
|
6702
|
+
import { join as join34 } from "path";
|
|
6680
6703
|
|
|
6681
6704
|
// src/templates/registry.ts
|
|
6682
6705
|
import fg4 from "fast-glob";
|
|
@@ -6684,8 +6707,8 @@ import { basename as basename4, relative as relative5 } from "pathe";
|
|
|
6684
6707
|
|
|
6685
6708
|
// src/onboarding/scaffold-generator.ts
|
|
6686
6709
|
var FEATURE_TEMPLATE_TARGETS = [
|
|
6687
|
-
["business.md.hbs",
|
|
6688
|
-
["technical.md.hbs",
|
|
6710
|
+
["business.md.hbs", join34(PATHS.MODULE_FEATURES_DIR, "core", "business.md")],
|
|
6711
|
+
["technical.md.hbs", join34(PATHS.MODULE_FEATURES_DIR, "core", "technical.md")]
|
|
6689
6712
|
];
|
|
6690
6713
|
|
|
6691
6714
|
// src/packs/manager.ts
|
|
@@ -6699,14 +6722,14 @@ import {
|
|
|
6699
6722
|
writeFileSync as writeFileSync5
|
|
6700
6723
|
} from "fs";
|
|
6701
6724
|
import { homedir as homedir2 } from "os";
|
|
6702
|
-
import { join as
|
|
6725
|
+
import { join as join35, resolve as resolve2 } from "path";
|
|
6703
6726
|
import { execa } from "execa";
|
|
6704
6727
|
var SOURCE_ORDER2 = ["built-in", "global", "project"];
|
|
6705
6728
|
function resolvePackManagerRoots(projectRoot = process.cwd(), overrides = {}) {
|
|
6706
6729
|
return {
|
|
6707
6730
|
runtimeRoot: overrides.runtimeRoot ?? getRuntimeRoot(),
|
|
6708
|
-
globalPacksRoot: overrides.globalPacksRoot ?? process.env.PAQAD_GLOBAL_PACKS_ROOT ??
|
|
6709
|
-
projectPacksRoot: overrides.projectPacksRoot ??
|
|
6731
|
+
globalPacksRoot: overrides.globalPacksRoot ?? process.env.PAQAD_GLOBAL_PACKS_ROOT ?? join35(homedir2(), ".paqad", "packs"),
|
|
6732
|
+
projectPacksRoot: overrides.projectPacksRoot ?? join35(projectRoot, ".paqad", "packs"),
|
|
6710
6733
|
registryUrl: overrides.registryUrl ?? process.env.PAQAD_PACK_REGISTRY_URL
|
|
6711
6734
|
};
|
|
6712
6735
|
}
|
|
@@ -6754,7 +6777,7 @@ async function installPack(source, options = {}) {
|
|
|
6754
6777
|
if (!pack.validation.valid) {
|
|
6755
6778
|
throw new Error(formatValidationIssues(pack.validation.issues));
|
|
6756
6779
|
}
|
|
6757
|
-
const destination =
|
|
6780
|
+
const destination = join35(installRoot, pack.manifest.name);
|
|
6758
6781
|
rmSync2(destination, { recursive: true, force: true });
|
|
6759
6782
|
cpSync(candidateRoot, destination, { recursive: true });
|
|
6760
6783
|
const installed = loader.validatePack(destination, scope === "project" ? "project" : "global");
|
|
@@ -6766,12 +6789,12 @@ async function installPack(source, options = {}) {
|
|
|
6766
6789
|
function removePack(name, projectRoot = process.cwd(), scope = "global", overrides = {}) {
|
|
6767
6790
|
const roots = resolvePackManagerRoots(projectRoot, overrides);
|
|
6768
6791
|
const targetRoot = scope === "project" ? roots.projectPacksRoot : roots.globalPacksRoot;
|
|
6769
|
-
const target =
|
|
6792
|
+
const target = join35(targetRoot, name);
|
|
6770
6793
|
if (existsSync15(target)) {
|
|
6771
6794
|
rmSync2(target, { recursive: true, force: true });
|
|
6772
6795
|
return;
|
|
6773
6796
|
}
|
|
6774
|
-
const builtInRoot =
|
|
6797
|
+
const builtInRoot = join35(roots.runtimeRoot, "capabilities", "coding", "stacks", name);
|
|
6775
6798
|
if (existsSync15(builtInRoot)) {
|
|
6776
6799
|
throw new Error(
|
|
6777
6800
|
`Cannot remove built-in pack "${name}"; remove a global or project override instead`
|
|
@@ -6789,14 +6812,14 @@ function validatePackAt(path) {
|
|
|
6789
6812
|
function createPack(name, options = {}) {
|
|
6790
6813
|
const destinationRoot = options.destinationRoot ?? process.cwd();
|
|
6791
6814
|
const ecosystem = options.ecosystem ?? "node";
|
|
6792
|
-
const packRoot =
|
|
6815
|
+
const packRoot = join35(destinationRoot, name);
|
|
6793
6816
|
if (existsSync15(packRoot)) {
|
|
6794
6817
|
throw new Error(`Pack scaffold already exists at ${packRoot}`);
|
|
6795
6818
|
}
|
|
6796
|
-
mkdirSync5(
|
|
6797
|
-
writeFileSync5(
|
|
6819
|
+
mkdirSync5(join35(packRoot, "rules"), { recursive: true });
|
|
6820
|
+
writeFileSync5(join35(packRoot, "pack.yaml"), renderPackTemplate(name, ecosystem));
|
|
6798
6821
|
writeFileSync5(
|
|
6799
|
-
|
|
6822
|
+
join35(packRoot, "rules", "conventions.md"),
|
|
6800
6823
|
`# ${name}
|
|
6801
6824
|
|
|
6802
6825
|
Document project-specific conventions for the ${name} stack here.
|
|
@@ -6813,7 +6836,7 @@ function listPackNamesForSource(source, roots) {
|
|
|
6813
6836
|
}
|
|
6814
6837
|
function resolveSourceRoot(source, roots) {
|
|
6815
6838
|
if (source === "built-in") {
|
|
6816
|
-
return
|
|
6839
|
+
return join35(roots.runtimeRoot, "capabilities", "coding", "stacks");
|
|
6817
6840
|
}
|
|
6818
6841
|
return source === "global" ? roots.globalPacksRoot : roots.projectPacksRoot;
|
|
6819
6842
|
}
|
|
@@ -6839,7 +6862,7 @@ function looksLikeGitUrl(source) {
|
|
|
6839
6862
|
return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("ssh://") || source.startsWith("git@") || source.startsWith("file://") || source.endsWith(".git");
|
|
6840
6863
|
}
|
|
6841
6864
|
async function clonePackSource(source) {
|
|
6842
|
-
const tempRoot = mkdtempSync(
|
|
6865
|
+
const tempRoot = mkdtempSync(join35(homedir2(), ".paqad-pack-clone-"));
|
|
6843
6866
|
try {
|
|
6844
6867
|
await execa("git", ["clone", "--depth", "1", source, tempRoot]);
|
|
6845
6868
|
} catch (error) {
|
|
@@ -6849,11 +6872,11 @@ async function clonePackSource(source) {
|
|
|
6849
6872
|
return findPackRoot(tempRoot);
|
|
6850
6873
|
}
|
|
6851
6874
|
function findPackRoot(root) {
|
|
6852
|
-
const rootManifest =
|
|
6875
|
+
const rootManifest = join35(root, "pack.yaml");
|
|
6853
6876
|
if (existsSync15(rootManifest)) {
|
|
6854
6877
|
return root;
|
|
6855
6878
|
}
|
|
6856
|
-
const candidates = readdirSync3(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
6879
|
+
const candidates = readdirSync3(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join35(root, entry.name)).filter((candidate) => existsSync15(join35(candidate, "pack.yaml")));
|
|
6857
6880
|
if (candidates.length === 1) {
|
|
6858
6881
|
return candidates[0];
|
|
6859
6882
|
}
|
|
@@ -6954,13 +6977,13 @@ async function queryOsv(packages) {
|
|
|
6954
6977
|
// src/pentest/progress-tracker.ts
|
|
6955
6978
|
import { existsSync as existsSync17 } from "fs";
|
|
6956
6979
|
import { mkdir as mkdir12, readdir as readdir4, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
|
|
6957
|
-
import { dirname as dirname17, join as
|
|
6980
|
+
import { dirname as dirname17, join as join37 } from "path";
|
|
6958
6981
|
|
|
6959
6982
|
// src/pentest/shared.ts
|
|
6960
6983
|
import { createHash as createHash7 } from "crypto";
|
|
6961
6984
|
import { existsSync as existsSync16 } from "fs";
|
|
6962
6985
|
import { mkdir as mkdir11, readFile as readFile15, readdir as readdir3, writeFile as writeFile11 } from "fs/promises";
|
|
6963
|
-
import { basename as basename5, dirname as dirname16, join as
|
|
6986
|
+
import { basename as basename5, dirname as dirname16, join as join36, relative as relative6 } from "path";
|
|
6964
6987
|
import { execa as execa2 } from "execa";
|
|
6965
6988
|
import fg5 from "fast-glob";
|
|
6966
6989
|
function toLocalTimestamp(date) {
|
|
@@ -7024,7 +7047,7 @@ async function discoverTargetUrl(projectRoot, stack, explicit) {
|
|
|
7024
7047
|
}
|
|
7025
7048
|
const envFiles = [".env", ".env.local", ".env.example"];
|
|
7026
7049
|
for (const envFile of envFiles) {
|
|
7027
|
-
const path =
|
|
7050
|
+
const path = join36(projectRoot, envFile);
|
|
7028
7051
|
if (!existsSync16(path)) {
|
|
7029
7052
|
continue;
|
|
7030
7053
|
}
|
|
@@ -7046,9 +7069,9 @@ async function discoverTargetUrl(projectRoot, stack, explicit) {
|
|
|
7046
7069
|
return null;
|
|
7047
7070
|
}
|
|
7048
7071
|
async function runProjectScript(projectRoot, scriptName, env, logDir) {
|
|
7049
|
-
const scriptPath =
|
|
7050
|
-
const stdoutPath =
|
|
7051
|
-
const stderrPath =
|
|
7072
|
+
const scriptPath = join36(projectRoot, PATHS.SCRIPTS_DIR, scriptName);
|
|
7073
|
+
const stdoutPath = join36(logDir, `${scriptName}.stdout.log`);
|
|
7074
|
+
const stderrPath = join36(logDir, `${scriptName}.stderr.log`);
|
|
7052
7075
|
await mkdir11(logDir, { recursive: true });
|
|
7053
7076
|
if (!existsSync16(scriptPath)) {
|
|
7054
7077
|
await writeFile11(stdoutPath, "");
|
|
@@ -7078,7 +7101,7 @@ async function runProjectScript(projectRoot, scriptName, env, logDir) {
|
|
|
7078
7101
|
};
|
|
7079
7102
|
}
|
|
7080
7103
|
async function loadModuleDocs(projectRoot, focusModules = []) {
|
|
7081
|
-
const moduleRoot =
|
|
7104
|
+
const moduleRoot = join36(projectRoot, PATHS.MODULES_DIR);
|
|
7082
7105
|
if (!existsSync16(moduleRoot)) {
|
|
7083
7106
|
return [];
|
|
7084
7107
|
}
|
|
@@ -7147,7 +7170,7 @@ function inferSourceArtifacts(baseDir, scriptResults) {
|
|
|
7147
7170
|
return [
|
|
7148
7171
|
...new Set(
|
|
7149
7172
|
artifacts.map(
|
|
7150
|
-
(artifact) => artifact.startsWith(".") ? artifact : relative6(dirname16(baseDir),
|
|
7173
|
+
(artifact) => artifact.startsWith(".") ? artifact : relative6(dirname16(baseDir), join36(dirname16(baseDir), artifact))
|
|
7151
7174
|
)
|
|
7152
7175
|
)
|
|
7153
7176
|
];
|
|
@@ -7221,7 +7244,7 @@ var PentestProgressTracker = class {
|
|
|
7221
7244
|
};
|
|
7222
7245
|
}
|
|
7223
7246
|
async load(projectRoot, runId) {
|
|
7224
|
-
const target =
|
|
7247
|
+
const target = join37(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "progress.json");
|
|
7225
7248
|
if (!existsSync17(target)) {
|
|
7226
7249
|
return null;
|
|
7227
7250
|
}
|
|
@@ -7233,7 +7256,7 @@ var PentestProgressTracker = class {
|
|
|
7233
7256
|
return parsed;
|
|
7234
7257
|
}
|
|
7235
7258
|
async save(projectRoot, progress) {
|
|
7236
|
-
const target =
|
|
7259
|
+
const target = join37(projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "progress.json");
|
|
7237
7260
|
progress.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7238
7261
|
await mkdir12(dirname17(target), { recursive: true });
|
|
7239
7262
|
await writeFile12(target, `${JSON.stringify(progress, null, 2)}
|
|
@@ -7292,7 +7315,7 @@ var PentestProgressTracker = class {
|
|
|
7292
7315
|
return step.status === "completed" && step.input_hash === inputHash;
|
|
7293
7316
|
}
|
|
7294
7317
|
async findIncompleteRun(projectRoot, workflow, sourceReportPath) {
|
|
7295
|
-
const runsDir =
|
|
7318
|
+
const runsDir = join37(projectRoot, PATHS.PENTEST_RUNS_DIR);
|
|
7296
7319
|
if (!existsSync17(runsDir)) {
|
|
7297
7320
|
return null;
|
|
7298
7321
|
}
|
|
@@ -7335,15 +7358,15 @@ var PentestProgressTracker = class {
|
|
|
7335
7358
|
}
|
|
7336
7359
|
};
|
|
7337
7360
|
function runArtifactsDir(projectRoot, runId) {
|
|
7338
|
-
return
|
|
7361
|
+
return join37(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "artifacts");
|
|
7339
7362
|
}
|
|
7340
7363
|
function runLogsDir(projectRoot, runId) {
|
|
7341
|
-
return
|
|
7364
|
+
return join37(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "logs");
|
|
7342
7365
|
}
|
|
7343
7366
|
|
|
7344
7367
|
// src/pipeline/lane-runner.ts
|
|
7345
7368
|
import { mkdir as mkdir16, writeFile as writeFile16 } from "fs/promises";
|
|
7346
|
-
import { dirname as dirname20, join as
|
|
7369
|
+
import { dirname as dirname20, join as join43 } from "path";
|
|
7347
7370
|
|
|
7348
7371
|
// src/pipeline/phases/shared.ts
|
|
7349
7372
|
function createPassResult(phase, summary, context, artifacts = [`handoff:${context.phases.length + 1}`]) {
|
|
@@ -7463,12 +7486,12 @@ var LoadDocsPhase = class {
|
|
|
7463
7486
|
|
|
7464
7487
|
// src/workflows/pentest.ts
|
|
7465
7488
|
import { mkdir as mkdir13, writeFile as writeFile13 } from "fs/promises";
|
|
7466
|
-
import { join as
|
|
7489
|
+
import { join as join40, relative as relative7 } from "path";
|
|
7467
7490
|
|
|
7468
7491
|
// src/pentest/findings.ts
|
|
7469
7492
|
import { existsSync as existsSync18 } from "fs";
|
|
7470
7493
|
import { readFile as readFile17 } from "fs/promises";
|
|
7471
|
-
import { join as
|
|
7494
|
+
import { join as join38 } from "path";
|
|
7472
7495
|
function createFinding(input) {
|
|
7473
7496
|
return {
|
|
7474
7497
|
id: "",
|
|
@@ -7638,11 +7661,264 @@ function buildModuleFindings(docs, tests) {
|
|
|
7638
7661
|
);
|
|
7639
7662
|
}
|
|
7640
7663
|
}
|
|
7664
|
+
const authMechanismTests = extractRelevantTestPaths(tests, moduleDoc.module, [
|
|
7665
|
+
"algorithm",
|
|
7666
|
+
"expir",
|
|
7667
|
+
"signature",
|
|
7668
|
+
"refresh",
|
|
7669
|
+
"revok",
|
|
7670
|
+
"logout",
|
|
7671
|
+
"session.invalidat",
|
|
7672
|
+
"alg:none",
|
|
7673
|
+
"jwt"
|
|
7674
|
+
]);
|
|
7675
|
+
if (hasAtLeast(lowered, 2, [
|
|
7676
|
+
"jwt",
|
|
7677
|
+
"token",
|
|
7678
|
+
"bearer",
|
|
7679
|
+
"session",
|
|
7680
|
+
"cookie",
|
|
7681
|
+
"oauth",
|
|
7682
|
+
"saml",
|
|
7683
|
+
"oidc",
|
|
7684
|
+
"sanctum"
|
|
7685
|
+
])) {
|
|
7686
|
+
if (authMechanismTests.length === 0) {
|
|
7687
|
+
findings.push(
|
|
7688
|
+
createFinding({
|
|
7689
|
+
title: `Auth token lifecycle checks are thin for ${moduleDoc.module}`,
|
|
7690
|
+
description: "Module docs describe authentication tokens, sessions, or OAuth flows, but the current tests do not show evidence of token expiry, signature validation, session invalidation, or logout handling.",
|
|
7691
|
+
impact: "high",
|
|
7692
|
+
effort: "medium",
|
|
7693
|
+
possible_solution_direction: "Add tests for token expiry enforcement, alg:none rejection, session invalidation on logout, and refresh token rotation for the documented auth flows.",
|
|
7694
|
+
how_to_reproduce: [
|
|
7695
|
+
`Review ${moduleDoc.paths.join(", ")} for token or session handling.`,
|
|
7696
|
+
"Search tests for algorithm, expiry, signature, revocation, and logout coverage for that module."
|
|
7697
|
+
],
|
|
7698
|
+
impact_area: [...moduleDoc.paths, `module:${moduleDoc.module}`],
|
|
7699
|
+
evidence: [
|
|
7700
|
+
...moduleDoc.paths.map((path) => `Doc evidence: ${path}`),
|
|
7701
|
+
"No matching auth token lifecycle test evidence found for this module."
|
|
7702
|
+
],
|
|
7703
|
+
category: "auth-mechanism",
|
|
7704
|
+
status: "open",
|
|
7705
|
+
confidence: 0.71,
|
|
7706
|
+
source_types: ["docs", "tests"],
|
|
7707
|
+
affected_modules: [moduleDoc.module],
|
|
7708
|
+
affected_packages: [],
|
|
7709
|
+
runtime_required: false,
|
|
7710
|
+
manual_follow_up: true
|
|
7711
|
+
})
|
|
7712
|
+
);
|
|
7713
|
+
}
|
|
7714
|
+
}
|
|
7715
|
+
const ssrfTests = extractRelevantTestPaths(tests, moduleDoc.module, [
|
|
7716
|
+
"ssrf",
|
|
7717
|
+
"redirect",
|
|
7718
|
+
"allowlist",
|
|
7719
|
+
"block",
|
|
7720
|
+
"internal",
|
|
7721
|
+
"private",
|
|
7722
|
+
"localhost",
|
|
7723
|
+
"metadata"
|
|
7724
|
+
]);
|
|
7725
|
+
if (hasAtLeast(lowered, 2, [
|
|
7726
|
+
"url",
|
|
7727
|
+
"redirect",
|
|
7728
|
+
"callback",
|
|
7729
|
+
"webhook",
|
|
7730
|
+
"fetch",
|
|
7731
|
+
"http",
|
|
7732
|
+
"proxy",
|
|
7733
|
+
"curl",
|
|
7734
|
+
"axios"
|
|
7735
|
+
])) {
|
|
7736
|
+
if (ssrfTests.length === 0) {
|
|
7737
|
+
findings.push(
|
|
7738
|
+
createFinding({
|
|
7739
|
+
title: `Outbound request / redirect surface lacks abuse validation for ${moduleDoc.module}`,
|
|
7740
|
+
description: "Module docs describe outbound HTTP calls, URL parameters, webhooks, or redirect flows, but the current tests do not show evidence of SSRF prevention, redirect allowlist validation, or private-range blocking.",
|
|
7741
|
+
impact: "high",
|
|
7742
|
+
effort: "medium",
|
|
7743
|
+
possible_solution_direction: "Add tests that verify SSRF prevention (blocked private ranges, redirect chain validation) and open-redirect protection for the documented outbound request surfaces.",
|
|
7744
|
+
how_to_reproduce: [
|
|
7745
|
+
`Review ${moduleDoc.paths.join(", ")} for URL, redirect, or webhook parameter handling.`,
|
|
7746
|
+
"Test with SSRF payloads: 127.0.0.1, 169.254.169.254, [::1], redirect chain to internal IP."
|
|
7747
|
+
],
|
|
7748
|
+
impact_area: [...moduleDoc.paths, `module:${moduleDoc.module}`],
|
|
7749
|
+
evidence: [
|
|
7750
|
+
...moduleDoc.paths.map((path) => `Doc evidence: ${path}`),
|
|
7751
|
+
"No matching SSRF or redirect validation test evidence found for this module."
|
|
7752
|
+
],
|
|
7753
|
+
category: "input-validation",
|
|
7754
|
+
status: "open",
|
|
7755
|
+
confidence: 0.7,
|
|
7756
|
+
source_types: ["docs", "tests"],
|
|
7757
|
+
affected_modules: [moduleDoc.module],
|
|
7758
|
+
affected_packages: [],
|
|
7759
|
+
runtime_required: false,
|
|
7760
|
+
manual_follow_up: true
|
|
7761
|
+
})
|
|
7762
|
+
);
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
const massAssignTests = extractRelevantTestPaths(tests, moduleDoc.module, [
|
|
7766
|
+
"mass",
|
|
7767
|
+
"assign",
|
|
7768
|
+
"fillable",
|
|
7769
|
+
"guarded",
|
|
7770
|
+
"allowlist",
|
|
7771
|
+
"forbidden",
|
|
7772
|
+
"attr_protected",
|
|
7773
|
+
"protected"
|
|
7774
|
+
]);
|
|
7775
|
+
if (hasAtLeast(lowered, 2, [
|
|
7776
|
+
"fill",
|
|
7777
|
+
"create",
|
|
7778
|
+
"update",
|
|
7779
|
+
"patch",
|
|
7780
|
+
"assign",
|
|
7781
|
+
"body",
|
|
7782
|
+
"request",
|
|
7783
|
+
"fillable",
|
|
7784
|
+
"guarded"
|
|
7785
|
+
])) {
|
|
7786
|
+
if (massAssignTests.length === 0) {
|
|
7787
|
+
findings.push(
|
|
7788
|
+
createFinding({
|
|
7789
|
+
title: `Mass assignment protection evidence is absent for ${moduleDoc.module}`,
|
|
7790
|
+
description: "Module docs describe model creation or update flows accepting request body fields, but the current tests do not show evidence of mass assignment protection (fillable allowlist, guarded fields, or forbidden-field rejection).",
|
|
7791
|
+
impact: "medium",
|
|
7792
|
+
effort: "low",
|
|
7793
|
+
possible_solution_direction: "Verify the model uses an explicit $fillable allowlist or $guarded blocklist. Add tests that confirm unintended fields (is_admin, role, price, balance) are rejected when submitted in the request body.",
|
|
7794
|
+
how_to_reproduce: [
|
|
7795
|
+
`Review ${moduleDoc.paths.join(", ")} for model create/update operations.`,
|
|
7796
|
+
"Submit a PATCH/POST request with extra privileged fields: is_admin=true, role=admin, price=0."
|
|
7797
|
+
],
|
|
7798
|
+
impact_area: [...moduleDoc.paths, `module:${moduleDoc.module}`],
|
|
7799
|
+
evidence: [
|
|
7800
|
+
...moduleDoc.paths.map((path) => `Doc evidence: ${path}`),
|
|
7801
|
+
"No matching mass assignment protection test evidence found for this module."
|
|
7802
|
+
],
|
|
7803
|
+
category: "input-validation",
|
|
7804
|
+
status: "open",
|
|
7805
|
+
confidence: 0.68,
|
|
7806
|
+
source_types: ["docs", "tests"],
|
|
7807
|
+
affected_modules: [moduleDoc.module],
|
|
7808
|
+
affected_packages: [],
|
|
7809
|
+
runtime_required: false,
|
|
7810
|
+
manual_follow_up: false
|
|
7811
|
+
})
|
|
7812
|
+
);
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
const cryptoTests = extractRelevantTestPaths(tests, moduleDoc.module, [
|
|
7816
|
+
"crypto",
|
|
7817
|
+
"encrypt",
|
|
7818
|
+
"hash.strength",
|
|
7819
|
+
"key.rotation",
|
|
7820
|
+
"salt",
|
|
7821
|
+
"entropy",
|
|
7822
|
+
"randomBytes",
|
|
7823
|
+
"bcrypt",
|
|
7824
|
+
"argon"
|
|
7825
|
+
]);
|
|
7826
|
+
if (hasAtLeast(lowered, 2, [
|
|
7827
|
+
"encrypt",
|
|
7828
|
+
"decrypt",
|
|
7829
|
+
"hash",
|
|
7830
|
+
"md5",
|
|
7831
|
+
"sha1",
|
|
7832
|
+
"secret",
|
|
7833
|
+
"key",
|
|
7834
|
+
"cipher",
|
|
7835
|
+
"aes",
|
|
7836
|
+
"rsa",
|
|
7837
|
+
"hmac"
|
|
7838
|
+
])) {
|
|
7839
|
+
if (cryptoTests.length === 0) {
|
|
7840
|
+
findings.push(
|
|
7841
|
+
createFinding({
|
|
7842
|
+
title: `Cryptographic implementation lacks verification coverage for ${moduleDoc.module}`,
|
|
7843
|
+
description: "Module docs describe encryption, hashing, or key management, but the current tests do not show evidence of cryptographic strength validation (algorithm checks, key rotation, salt uniqueness, or secure PRNG usage).",
|
|
7844
|
+
impact: "high",
|
|
7845
|
+
effort: "medium",
|
|
7846
|
+
possible_solution_direction: "Add tests that verify password hashing uses bcrypt/argon2 with a sufficient cost factor, encryption uses a secure algorithm with a randomly generated IV, and tokens are generated with a cryptographically secure PRNG.",
|
|
7847
|
+
how_to_reproduce: [
|
|
7848
|
+
`Review ${moduleDoc.paths.join(", ")} for cryptographic operations.`,
|
|
7849
|
+
"Check for MD5/SHA1 password hashing, hardcoded IVs, or Math.random() for token generation."
|
|
7850
|
+
],
|
|
7851
|
+
impact_area: [...moduleDoc.paths, `module:${moduleDoc.module}`],
|
|
7852
|
+
evidence: [
|
|
7853
|
+
...moduleDoc.paths.map((path) => `Doc evidence: ${path}`),
|
|
7854
|
+
"No matching cryptographic strength or key management test evidence found for this module."
|
|
7855
|
+
],
|
|
7856
|
+
category: "cryptographic",
|
|
7857
|
+
status: "open",
|
|
7858
|
+
confidence: 0.67,
|
|
7859
|
+
source_types: ["docs", "tests"],
|
|
7860
|
+
affected_modules: [moduleDoc.module],
|
|
7861
|
+
affected_packages: [],
|
|
7862
|
+
runtime_required: false,
|
|
7863
|
+
manual_follow_up: true
|
|
7864
|
+
})
|
|
7865
|
+
);
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
const loggingTests = extractRelevantTestPaths(tests, moduleDoc.module, [
|
|
7869
|
+
"audit",
|
|
7870
|
+
"log.assert",
|
|
7871
|
+
"event.dispatch",
|
|
7872
|
+
"trail",
|
|
7873
|
+
"activity",
|
|
7874
|
+
"log.entry",
|
|
7875
|
+
"logged",
|
|
7876
|
+
"event"
|
|
7877
|
+
]);
|
|
7878
|
+
if (hasAtLeast(lowered, 2, [
|
|
7879
|
+
"log",
|
|
7880
|
+
"audit",
|
|
7881
|
+
"event",
|
|
7882
|
+
"track",
|
|
7883
|
+
"monitor",
|
|
7884
|
+
"alert",
|
|
7885
|
+
"sentry",
|
|
7886
|
+
"activity"
|
|
7887
|
+
])) {
|
|
7888
|
+
if (loggingTests.length === 0) {
|
|
7889
|
+
findings.push(
|
|
7890
|
+
createFinding({
|
|
7891
|
+
title: `Security-relevant actions lack audit logging evidence for ${moduleDoc.module}`,
|
|
7892
|
+
description: "Module docs describe operations that should be audited (authentication, financial transactions, admin actions, or data exports), but the current tests do not show evidence of audit log assertions or event dispatch verification.",
|
|
7893
|
+
impact: "medium",
|
|
7894
|
+
effort: "medium",
|
|
7895
|
+
possible_solution_direction: "Add tests that assert audit log entries are written for high-value actions with actor identity, IP, and timestamp. Verify sensitive data (passwords, tokens) is not included in log output.",
|
|
7896
|
+
how_to_reproduce: [
|
|
7897
|
+
`Review ${moduleDoc.paths.join(", ")} for operations that must be auditable.`,
|
|
7898
|
+
"Check logging configuration and assert that log entries are created with sufficient context for forensic analysis."
|
|
7899
|
+
],
|
|
7900
|
+
impact_area: [...moduleDoc.paths, `module:${moduleDoc.module}`],
|
|
7901
|
+
evidence: [
|
|
7902
|
+
...moduleDoc.paths.map((path) => `Doc evidence: ${path}`),
|
|
7903
|
+
"No matching audit logging or event dispatch test evidence found for this module."
|
|
7904
|
+
],
|
|
7905
|
+
category: "logging-monitoring",
|
|
7906
|
+
status: "open",
|
|
7907
|
+
confidence: 0.65,
|
|
7908
|
+
source_types: ["docs", "tests"],
|
|
7909
|
+
affected_modules: [moduleDoc.module],
|
|
7910
|
+
affected_packages: [],
|
|
7911
|
+
runtime_required: false,
|
|
7912
|
+
manual_follow_up: false
|
|
7913
|
+
})
|
|
7914
|
+
);
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7641
7917
|
}
|
|
7642
7918
|
return findings;
|
|
7643
7919
|
}
|
|
7644
7920
|
async function buildSecretFindings(artifactDir) {
|
|
7645
|
-
const path =
|
|
7921
|
+
const path = join38(artifactDir, "secrets", "matches.txt");
|
|
7646
7922
|
if (!existsSync18(path)) {
|
|
7647
7923
|
return [];
|
|
7648
7924
|
}
|
|
@@ -7743,6 +8019,9 @@ function evaluateRetestStatus(sourceFinding, currentFindings, runtimeStatus, blo
|
|
|
7743
8019
|
function hasAny(content, tokens) {
|
|
7744
8020
|
return tokens.some((token) => content.includes(token));
|
|
7745
8021
|
}
|
|
8022
|
+
function hasAtLeast(content, count, tokens) {
|
|
8023
|
+
return tokens.filter((token) => content.includes(token)).length >= count;
|
|
8024
|
+
}
|
|
7746
8025
|
function normalizeImpact(content) {
|
|
7747
8026
|
const lowered = content.toLowerCase();
|
|
7748
8027
|
if (lowered.includes("critical") || lowered.includes("remote code execution") || lowered.includes("credential") || lowered.includes("auth bypass")) {
|
|
@@ -7755,8 +8034,8 @@ function normalizeImpact(content) {
|
|
|
7755
8034
|
}
|
|
7756
8035
|
async function readNativeAuditFindings(artifactDir) {
|
|
7757
8036
|
const findings = [];
|
|
7758
|
-
const dependencyDir =
|
|
7759
|
-
const npmAudit = await readJsonMaybe(
|
|
8037
|
+
const dependencyDir = join38(artifactDir, "dependencies");
|
|
8038
|
+
const npmAudit = await readJsonMaybe(join38(dependencyDir, "npm-audit.json"));
|
|
7760
8039
|
for (const [name, vulnerability] of Object.entries(npmAudit?.vulnerabilities ?? {})) {
|
|
7761
8040
|
const via = (vulnerability.via ?? []).find((entry) => typeof entry === "object");
|
|
7762
8041
|
findings.push({
|
|
@@ -7768,7 +8047,7 @@ async function readNativeAuditFindings(artifactDir) {
|
|
|
7768
8047
|
details: via?.url ?? ""
|
|
7769
8048
|
});
|
|
7770
8049
|
}
|
|
7771
|
-
const pnpmAudit = await readJsonMaybe(
|
|
8050
|
+
const pnpmAudit = await readJsonMaybe(join38(dependencyDir, "pnpm-audit.json"));
|
|
7772
8051
|
for (const advisory of Object.values(pnpmAudit?.advisories ?? {})) {
|
|
7773
8052
|
findings.push({
|
|
7774
8053
|
package_name: advisory.module_name ?? "unknown",
|
|
@@ -7779,7 +8058,7 @@ async function readNativeAuditFindings(artifactDir) {
|
|
|
7779
8058
|
details: advisory.overview ?? ""
|
|
7780
8059
|
});
|
|
7781
8060
|
}
|
|
7782
|
-
const composerAudit = await readJsonMaybe(
|
|
8061
|
+
const composerAudit = await readJsonMaybe(join38(dependencyDir, "composer-audit.json"));
|
|
7783
8062
|
if (Array.isArray(composerAudit?.advisories)) {
|
|
7784
8063
|
for (const advisory of composerAudit.advisories) {
|
|
7785
8064
|
findings.push({
|
|
@@ -7828,10 +8107,33 @@ async function readJsonMaybe(path) {
|
|
|
7828
8107
|
|
|
7829
8108
|
// src/pentest/file-check-mapper.ts
|
|
7830
8109
|
var GENERIC_SECURITY_MAP = [
|
|
7831
|
-
{
|
|
8110
|
+
{
|
|
8111
|
+
glob: "**/auth*",
|
|
8112
|
+
checks: ["permission-boundary-review", "runtime-surface-probing", "auth-mechanism-review"]
|
|
8113
|
+
},
|
|
7832
8114
|
{ glob: "**/guard*", checks: ["permission-boundary-review"] },
|
|
7833
|
-
{ glob: "**/.env*", checks: ["runtime-surface-probing"] },
|
|
7834
|
-
{ glob: "**/config*", checks: ["runtime-surface-probing"] }
|
|
8115
|
+
{ glob: "**/.env*", checks: ["runtime-surface-probing", "cryptographic-review"] },
|
|
8116
|
+
{ glob: "**/config*", checks: ["runtime-surface-probing"] },
|
|
8117
|
+
// Auth / session files
|
|
8118
|
+
{ glob: "**/jwt*", checks: ["auth-mechanism-review"] },
|
|
8119
|
+
{ glob: "**/token*", checks: ["auth-mechanism-review"] },
|
|
8120
|
+
{ glob: "**/passport*", checks: ["auth-mechanism-review"] },
|
|
8121
|
+
{ glob: "**/session*", checks: ["auth-mechanism-review"] },
|
|
8122
|
+
// GraphQL schema and resolvers
|
|
8123
|
+
{ glob: "**/*.graphql", checks: ["input-validation-review", "permission-boundary-review"] },
|
|
8124
|
+
{ glob: "**/schema.graphql", checks: ["input-validation-review", "permission-boundary-review"] },
|
|
8125
|
+
{ glob: "**/resolvers/**", checks: ["input-validation-review", "permission-boundary-review"] },
|
|
8126
|
+
// Logging and audit
|
|
8127
|
+
{ glob: "**/log*", checks: ["logging-monitoring-review"] },
|
|
8128
|
+
{ glob: "**/audit*", checks: ["logging-monitoring-review"] },
|
|
8129
|
+
{ glob: "**/monitor*", checks: ["logging-monitoring-review"] },
|
|
8130
|
+
// Cryptography
|
|
8131
|
+
{ glob: "**/*crypt*", checks: ["cryptographic-review"] },
|
|
8132
|
+
{ glob: "**/*cipher*", checks: ["cryptographic-review"] },
|
|
8133
|
+
{ glob: "**/*hash*", checks: ["cryptographic-review"] },
|
|
8134
|
+
// File uploads and input
|
|
8135
|
+
{ glob: "**/upload*", checks: ["input-validation-review"] },
|
|
8136
|
+
{ glob: "**/file*", checks: ["input-validation-review"] }
|
|
7835
8137
|
];
|
|
7836
8138
|
var FileCheckMapper = class {
|
|
7837
8139
|
constructor(frameworks, projectRoot) {
|
|
@@ -7892,7 +8194,7 @@ var FileCheckMapper = class {
|
|
|
7892
8194
|
|
|
7893
8195
|
// src/pentest/incremental-scanner.ts
|
|
7894
8196
|
import { readFile as readFile18 } from "fs/promises";
|
|
7895
|
-
import { join as
|
|
8197
|
+
import { join as join39 } from "path";
|
|
7896
8198
|
import { createHash as createHash8 } from "crypto";
|
|
7897
8199
|
import { execa as execa3 } from "execa";
|
|
7898
8200
|
var IncrementalScanner = class {
|
|
@@ -7926,7 +8228,7 @@ var IncrementalScanner = class {
|
|
|
7926
8228
|
}
|
|
7927
8229
|
}
|
|
7928
8230
|
async gitDiff(projectRoot, lastRunId) {
|
|
7929
|
-
const progressPath =
|
|
8231
|
+
const progressPath = join39(projectRoot, ".paqad", "pentest", "runs", lastRunId, "progress.json");
|
|
7930
8232
|
let baseCommit;
|
|
7931
8233
|
try {
|
|
7932
8234
|
const raw = await readFile18(progressPath, "utf8");
|
|
@@ -7943,7 +8245,7 @@ var IncrementalScanner = class {
|
|
|
7943
8245
|
return result.stdout.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
7944
8246
|
}
|
|
7945
8247
|
async hashDiff(projectRoot, lastRunId) {
|
|
7946
|
-
const manifestPath =
|
|
8248
|
+
const manifestPath = join39(projectRoot, ".paqad", "pentest", "runs", lastRunId, "progress.json");
|
|
7947
8249
|
let fileManifest = {};
|
|
7948
8250
|
try {
|
|
7949
8251
|
const raw = await readFile18(manifestPath, "utf8");
|
|
@@ -7955,7 +8257,7 @@ var IncrementalScanner = class {
|
|
|
7955
8257
|
const changed = [];
|
|
7956
8258
|
for (const [filePath, storedHash] of Object.entries(fileManifest)) {
|
|
7957
8259
|
try {
|
|
7958
|
-
const content = await readFile18(
|
|
8260
|
+
const content = await readFile18(join39(projectRoot, filePath), "utf8");
|
|
7959
8261
|
const currentHash = createHash8("sha256").update(content).digest("hex");
|
|
7960
8262
|
if (currentHash !== storedHash) {
|
|
7961
8263
|
changed.push(filePath);
|
|
@@ -7968,11 +8270,11 @@ var IncrementalScanner = class {
|
|
|
7968
8270
|
}
|
|
7969
8271
|
async warnIfFullScanStale(projectRoot, thresholdDays) {
|
|
7970
8272
|
try {
|
|
7971
|
-
const runsDir =
|
|
8273
|
+
const runsDir = join39(projectRoot, ".paqad", "pentest", "runs");
|
|
7972
8274
|
const { readdir: readdir8 } = await import("fs/promises");
|
|
7973
8275
|
const runs = await readdir8(runsDir).catch(() => []);
|
|
7974
8276
|
for (const run of runs.sort().reverse()) {
|
|
7975
|
-
const progressPath =
|
|
8277
|
+
const progressPath = join39(runsDir, run, "progress.json");
|
|
7976
8278
|
try {
|
|
7977
8279
|
const raw = await readFile18(progressPath, "utf8");
|
|
7978
8280
|
const progress = JSON.parse(raw);
|
|
@@ -8170,8 +8472,8 @@ var PentestWorkflow = class {
|
|
|
8170
8472
|
await this.tracker.save(options.projectRoot, progress);
|
|
8171
8473
|
const docs = await loadModuleDocs(options.projectRoot, focusModules);
|
|
8172
8474
|
const tests = await loadTests(options.projectRoot, focusModules);
|
|
8173
|
-
const docsPath =
|
|
8174
|
-
const testsPath =
|
|
8475
|
+
const docsPath = join40(artifactsDir, "docs-summary.json");
|
|
8476
|
+
const testsPath = join40(artifactsDir, "tests-summary.json");
|
|
8175
8477
|
await writeJson(docsPath, docs);
|
|
8176
8478
|
await writeJson(
|
|
8177
8479
|
testsPath,
|
|
@@ -8272,7 +8574,7 @@ var PentestWorkflow = class {
|
|
|
8272
8574
|
progressRunId: progress.run_id,
|
|
8273
8575
|
reportTimestamp: new Date(progress.started_at)
|
|
8274
8576
|
});
|
|
8275
|
-
const findingIndexPath =
|
|
8577
|
+
const findingIndexPath = join40(
|
|
8276
8578
|
options.projectRoot,
|
|
8277
8579
|
PATHS.PENTEST_RUNS_DIR,
|
|
8278
8580
|
progress.run_id,
|
|
@@ -8289,7 +8591,7 @@ var PentestWorkflow = class {
|
|
|
8289
8591
|
}
|
|
8290
8592
|
if (report === null) {
|
|
8291
8593
|
const existingSidecar = progress.sidecar_path ? await readJsonIfExists(
|
|
8292
|
-
|
|
8594
|
+
join40(options.projectRoot, progress.sidecar_path)
|
|
8293
8595
|
) : null;
|
|
8294
8596
|
report = existingSidecar;
|
|
8295
8597
|
}
|
|
@@ -8304,18 +8606,18 @@ var PentestWorkflow = class {
|
|
|
8304
8606
|
this.tracker.markStepRunning(progress, "write-report", writeHash);
|
|
8305
8607
|
await this.tracker.save(options.projectRoot, progress);
|
|
8306
8608
|
await writeJson(
|
|
8307
|
-
|
|
8609
|
+
join40(options.projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "report-preview.json"),
|
|
8308
8610
|
{ report_id: report.report_id, findings: report.findings.length }
|
|
8309
8611
|
);
|
|
8310
|
-
await writeJson(
|
|
8612
|
+
await writeJson(join40(options.projectRoot, report.sidecar_path), report);
|
|
8311
8613
|
await writeJson(
|
|
8312
|
-
|
|
8614
|
+
join40(options.projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "blocked-checks.json"),
|
|
8313
8615
|
report.blocked_checks
|
|
8314
8616
|
);
|
|
8315
|
-
await mkdir13(
|
|
8617
|
+
await mkdir13(join40(options.projectRoot, PATHS.PENTEST_DIR), { recursive: true });
|
|
8316
8618
|
const markdown = buildPentestMarkdown(report);
|
|
8317
|
-
await writeJson(
|
|
8318
|
-
await writeFile13(
|
|
8619
|
+
await writeJson(join40(options.projectRoot, report.sidecar_path), report);
|
|
8620
|
+
await writeFile13(join40(options.projectRoot, report.report_path), markdown);
|
|
8319
8621
|
this.tracker.markStepCompleted(
|
|
8320
8622
|
progress,
|
|
8321
8623
|
"write-report",
|
|
@@ -8342,7 +8644,7 @@ async function buildCurrentPentestReport(input) {
|
|
|
8342
8644
|
const docs = await loadModuleDocs(input.projectRoot, input.focusModules);
|
|
8343
8645
|
const tests = await loadTests(input.projectRoot, input.focusModules);
|
|
8344
8646
|
const osvFindings = await queryOsv(input.snapshot.packages);
|
|
8345
|
-
const osvPath =
|
|
8647
|
+
const osvPath = join40(input.artifactsDir, "dependencies", "osv-results.json");
|
|
8346
8648
|
await writeJson(osvPath, osvFindings);
|
|
8347
8649
|
const dependencyFindings = await buildDependencyFindings(
|
|
8348
8650
|
input.snapshot,
|
|
@@ -8351,7 +8653,7 @@ async function buildCurrentPentestReport(input) {
|
|
|
8351
8653
|
);
|
|
8352
8654
|
const moduleFindings = buildModuleFindings(docs, tests);
|
|
8353
8655
|
const secretFindings = await buildSecretFindings(input.artifactsDir);
|
|
8354
|
-
const runtimePayload = await readJsonIfExists(
|
|
8656
|
+
const runtimePayload = await readJsonIfExists(join40(input.artifactsDir, "runtime", "runtime-checks.json"));
|
|
8355
8657
|
const runtimeStatus = input.targetUrl ? runtimePayload?.reachable ? {
|
|
8356
8658
|
target_url: input.targetUrl,
|
|
8357
8659
|
status: "reachable",
|
|
@@ -8377,8 +8679,8 @@ async function buildCurrentPentestReport(input) {
|
|
|
8377
8679
|
...runtimeFindings
|
|
8378
8680
|
]);
|
|
8379
8681
|
const timestamp = toLocalTimestamp(input.reportTimestamp);
|
|
8380
|
-
const reportPath =
|
|
8381
|
-
const sidecarPath =
|
|
8682
|
+
const reportPath = join40(PATHS.PENTEST_DIR, `${timestamp}.md`);
|
|
8683
|
+
const sidecarPath = join40(PATHS.PENTEST_DIR, `${timestamp}.json`);
|
|
8382
8684
|
const stack = getPrimaryStack({
|
|
8383
8685
|
routing: { domain: "coding" },
|
|
8384
8686
|
stack_profile: input.snapshot.profile
|
|
@@ -8431,9 +8733,9 @@ async function buildCurrentPentestReport(input) {
|
|
|
8431
8733
|
raw_evidence_paths: [
|
|
8432
8734
|
relative7(input.projectRoot, osvPath),
|
|
8433
8735
|
...[
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8736
|
+
join40(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "finding-index.json"),
|
|
8737
|
+
join40(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "artifacts", "docs-summary.json"),
|
|
8738
|
+
join40(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "artifacts", "tests-summary.json")
|
|
8437
8739
|
]
|
|
8438
8740
|
]
|
|
8439
8741
|
};
|
|
@@ -8473,7 +8775,7 @@ var PentestPhase = class {
|
|
|
8473
8775
|
|
|
8474
8776
|
// src/workflows/pentest-retest.ts
|
|
8475
8777
|
import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
|
|
8476
|
-
import { dirname as dirname18, join as
|
|
8778
|
+
import { dirname as dirname18, join as join41 } from "path";
|
|
8477
8779
|
var RETEST_STEPS = [
|
|
8478
8780
|
{ id: "load-source-report", title: "Load source pentest report" },
|
|
8479
8781
|
{ id: "rerun-evidence", title: "Collect fresh evidence for source findings" },
|
|
@@ -8494,7 +8796,7 @@ var PentestRetestWorkflow = class {
|
|
|
8494
8796
|
}
|
|
8495
8797
|
const normalizedSourcePath = normalizeSidecarPath(sourceReportPath);
|
|
8496
8798
|
const sourceSidecar = await readJsonIfExists(
|
|
8497
|
-
|
|
8799
|
+
join41(options.projectRoot, normalizedSourcePath)
|
|
8498
8800
|
);
|
|
8499
8801
|
if (sourceSidecar === null) {
|
|
8500
8802
|
throw new Error(`Source pentest sidecar not found at ${normalizedSourcePath}`);
|
|
@@ -8519,12 +8821,12 @@ var PentestRetestWorkflow = class {
|
|
|
8519
8821
|
if (!this.tracker.shouldSkipStep(progress, "load-source-report", sourceHash)) {
|
|
8520
8822
|
this.tracker.markStepRunning(progress, "load-source-report", sourceHash);
|
|
8521
8823
|
await this.tracker.save(options.projectRoot, progress);
|
|
8522
|
-
const sourceCopy =
|
|
8824
|
+
const sourceCopy = join41(artifactsDir, "source-report.json");
|
|
8523
8825
|
await writeJson(sourceCopy, sourceSidecar);
|
|
8524
8826
|
this.tracker.markStepCompleted(
|
|
8525
8827
|
progress,
|
|
8526
8828
|
"load-source-report",
|
|
8527
|
-
[
|
|
8829
|
+
[join41(PATHS.PENTEST_RUNS_DIR, progress.run_id, "artifacts", "source-report.json")],
|
|
8528
8830
|
[...RETEST_SKILLS.source].map(skillPath)
|
|
8529
8831
|
);
|
|
8530
8832
|
await this.tracker.save(options.projectRoot, progress);
|
|
@@ -8541,7 +8843,7 @@ var PentestRetestWorkflow = class {
|
|
|
8541
8843
|
PENTEST_RUN_ID: progress.run_id,
|
|
8542
8844
|
PENTEST_ARTIFACT_DIR: artifactsDir,
|
|
8543
8845
|
PENTEST_TARGET_URL: targetUrl ?? "",
|
|
8544
|
-
PENTEST_SOURCE_REPORT:
|
|
8846
|
+
PENTEST_SOURCE_REPORT: join41(options.projectRoot, normalizedSourcePath),
|
|
8545
8847
|
PENTEST_DB_CONNECTION_NAME: options.dbConnectionName ?? ""
|
|
8546
8848
|
};
|
|
8547
8849
|
const scriptResults = await Promise.all([
|
|
@@ -8590,8 +8892,8 @@ var PentestRetestWorkflow = class {
|
|
|
8590
8892
|
...currentReport,
|
|
8591
8893
|
report_id: toReportId("RETEST", new Date(progress.started_at)),
|
|
8592
8894
|
workflow: "pentest-retest",
|
|
8593
|
-
report_path:
|
|
8594
|
-
sidecar_path:
|
|
8895
|
+
report_path: join41(PATHS.PENTEST_RETEST_DIR, `${timestamp}-${sourceSlug}.md`),
|
|
8896
|
+
sidecar_path: join41(PATHS.PENTEST_RETEST_DIR, `${timestamp}-${sourceSlug}.json`),
|
|
8595
8897
|
source_report_path: normalizedSourcePath,
|
|
8596
8898
|
source_report_id: sourceSidecar.report_id,
|
|
8597
8899
|
findings: retestFindings,
|
|
@@ -8604,7 +8906,7 @@ var PentestRetestWorkflow = class {
|
|
|
8604
8906
|
...[...RETEST_SKILLS.source, ...RETEST_SKILLS.evaluate].map(skillPath)
|
|
8605
8907
|
]
|
|
8606
8908
|
};
|
|
8607
|
-
const findingIndexPath =
|
|
8909
|
+
const findingIndexPath = join41(
|
|
8608
8910
|
options.projectRoot,
|
|
8609
8911
|
PATHS.PENTEST_RUNS_DIR,
|
|
8610
8912
|
progress.run_id,
|
|
@@ -8614,14 +8916,14 @@ var PentestRetestWorkflow = class {
|
|
|
8614
8916
|
this.tracker.markStepCompleted(
|
|
8615
8917
|
progress,
|
|
8616
8918
|
"evaluate-source-findings",
|
|
8617
|
-
[
|
|
8919
|
+
[join41(PATHS.PENTEST_RUNS_DIR, progress.run_id, "finding-index.json")],
|
|
8618
8920
|
[...RETEST_SKILLS.evaluate].map(skillPath)
|
|
8619
8921
|
);
|
|
8620
8922
|
await this.tracker.save(options.projectRoot, progress);
|
|
8621
8923
|
}
|
|
8622
8924
|
if (retestReport === null) {
|
|
8623
8925
|
const existing = progress.sidecar_path ? await readJsonIfExists(
|
|
8624
|
-
|
|
8926
|
+
join41(options.projectRoot, progress.sidecar_path)
|
|
8625
8927
|
) : null;
|
|
8626
8928
|
retestReport = existing;
|
|
8627
8929
|
}
|
|
@@ -8635,12 +8937,12 @@ var PentestRetestWorkflow = class {
|
|
|
8635
8937
|
if (!this.tracker.shouldSkipStep(progress, "write-report", writeHash)) {
|
|
8636
8938
|
this.tracker.markStepRunning(progress, "write-report", writeHash);
|
|
8637
8939
|
await this.tracker.save(options.projectRoot, progress);
|
|
8638
|
-
await writeJson(
|
|
8639
|
-
await mkdir14(dirname18(
|
|
8940
|
+
await writeJson(join41(options.projectRoot, retestReport.sidecar_path), retestReport);
|
|
8941
|
+
await mkdir14(dirname18(join41(options.projectRoot, retestReport.report_path)), {
|
|
8640
8942
|
recursive: true
|
|
8641
8943
|
});
|
|
8642
8944
|
await writeFile14(
|
|
8643
|
-
|
|
8945
|
+
join41(options.projectRoot, retestReport.report_path),
|
|
8644
8946
|
buildPentestMarkdown(retestReport)
|
|
8645
8947
|
);
|
|
8646
8948
|
this.tracker.markStepCompleted(
|
|
@@ -8718,7 +9020,7 @@ var ProjectQuestionPhase = class {
|
|
|
8718
9020
|
|
|
8719
9021
|
// src/workflows/root-cause-analysis.ts
|
|
8720
9022
|
import { mkdir as mkdir15, writeFile as writeFile15 } from "fs/promises";
|
|
8721
|
-
import { dirname as dirname19, join as
|
|
9023
|
+
import { dirname as dirname19, join as join42 } from "path";
|
|
8722
9024
|
var DEFAULT_TITLE_SLUG = "root-cause-analysis";
|
|
8723
9025
|
var RCA_SECTIONS = [
|
|
8724
9026
|
"Summary",
|
|
@@ -8737,8 +9039,8 @@ var RootCauseAnalysisWorkflow = class {
|
|
|
8737
9039
|
async run(options) {
|
|
8738
9040
|
const title = deriveTitle(options.classification.request_text);
|
|
8739
9041
|
const filename = `${toLocalTimestamp2(/* @__PURE__ */ new Date())}-${slugify2(title) || DEFAULT_TITLE_SLUG}.md`;
|
|
8740
|
-
const relativePath =
|
|
8741
|
-
const outputPath =
|
|
9042
|
+
const relativePath = join42(PATHS.RCA_DIR, filename);
|
|
9043
|
+
const outputPath = join42(options.projectRoot, relativePath);
|
|
8742
9044
|
await mkdir15(dirname19(outputPath), { recursive: true });
|
|
8743
9045
|
await writeFile15(outputPath, buildRcaDocument(title, options.classification));
|
|
8744
9046
|
return {
|
|
@@ -8887,22 +9189,22 @@ var DEFAULT_PHASES = {
|
|
|
8887
9189
|
|
|
8888
9190
|
// src/pipeline/stream-truncator.ts
|
|
8889
9191
|
import { appendFile, mkdir as mkdir17 } from "fs/promises";
|
|
8890
|
-
import { join as
|
|
9192
|
+
import { join as join44, dirname as dirname21 } from "path";
|
|
8891
9193
|
|
|
8892
9194
|
// src/resolver/deduplicator.ts
|
|
8893
9195
|
import { createHash as createHash9 } from "crypto";
|
|
8894
9196
|
import { readFile as readFile19, writeFile as writeFile17, mkdir as mkdir18 } from "fs/promises";
|
|
8895
|
-
import { join as
|
|
9197
|
+
import { join as join45, dirname as dirname22 } from "path";
|
|
8896
9198
|
|
|
8897
9199
|
// src/scripts/generator.ts
|
|
8898
9200
|
import { chmodSync as chmodSync2 } from "fs";
|
|
8899
9201
|
import { mkdir as mkdir19, writeFile as writeFile18 } from "fs/promises";
|
|
8900
|
-
import { dirname as dirname23, join as
|
|
9202
|
+
import { dirname as dirname23, join as join46 } from "path";
|
|
8901
9203
|
|
|
8902
9204
|
// src/skills/cache-manager.ts
|
|
8903
9205
|
import { createHash as createHash10 } from "crypto";
|
|
8904
9206
|
import { mkdir as mkdir20, readFile as readFile20, readdir as readdir5, rm as rm2, stat as stat3, writeFile as writeFile19 } from "fs/promises";
|
|
8905
|
-
import { join as
|
|
9207
|
+
import { join as join47 } from "path";
|
|
8906
9208
|
import fg6 from "fast-glob";
|
|
8907
9209
|
|
|
8908
9210
|
// src/skills/frontmatter-parser.ts
|
|
@@ -8927,24 +9229,49 @@ var conditionalProcessor = new ConditionalSectionProcessor();
|
|
|
8927
9229
|
|
|
8928
9230
|
// src/skills/index-generator.ts
|
|
8929
9231
|
import { mkdir as mkdir21, readFile as readFile21, writeFile as writeFile20 } from "fs/promises";
|
|
8930
|
-
import { dirname as dirname24, join as
|
|
9232
|
+
import { dirname as dirname24, join as join48, relative as relative8 } from "path";
|
|
8931
9233
|
import fg7 from "fast-glob";
|
|
8932
9234
|
|
|
8933
9235
|
// src/skills/loader.ts
|
|
8934
9236
|
import { readFile as readFile22 } from "fs/promises";
|
|
8935
9237
|
import { basename as basename6 } from "pathe";
|
|
8936
9238
|
|
|
9239
|
+
// src/update/audit.ts
|
|
9240
|
+
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
9241
|
+
import { dirname as dirname25, join as join49 } from "path";
|
|
9242
|
+
function auditPath(projectRoot) {
|
|
9243
|
+
return join49(projectRoot, PATHS.AUDIT_LOG);
|
|
9244
|
+
}
|
|
9245
|
+
function ts() {
|
|
9246
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9247
|
+
}
|
|
9248
|
+
function appendAuditLog2(projectRoot, previous, updated) {
|
|
9249
|
+
const path = auditPath(projectRoot);
|
|
9250
|
+
mkdirSync6(dirname25(path), { recursive: true });
|
|
9251
|
+
const line = `[${ts()}] INFO silent-update previous=${previous ?? "unknown"} updated=${updated}
|
|
9252
|
+
`;
|
|
9253
|
+
appendFileSync(path, line);
|
|
9254
|
+
}
|
|
9255
|
+
function appendAuditLogFailure(projectRoot, previous, target, error) {
|
|
9256
|
+
const path = auditPath(projectRoot);
|
|
9257
|
+
mkdirSync6(dirname25(path), { recursive: true });
|
|
9258
|
+
const sanitized = error.replace(/"/g, "'");
|
|
9259
|
+
const line = `[${ts()}] WARN silent-update-failed previous=${previous ?? "unknown"} target=${target} error="${sanitized}"
|
|
9260
|
+
`;
|
|
9261
|
+
appendFileSync(path, line);
|
|
9262
|
+
}
|
|
9263
|
+
|
|
8937
9264
|
// src/update/updater.ts
|
|
8938
|
-
import { chmodSync as chmodSync3, existsSync as existsSync19, mkdirSync as
|
|
9265
|
+
import { chmodSync as chmodSync3, existsSync as existsSync19, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
8939
9266
|
import { mkdtemp, readdir as readdir6, readFile as readFile23, rm as rm3 } from "fs/promises";
|
|
8940
9267
|
import { tmpdir } from "os";
|
|
8941
|
-
import { dirname as
|
|
9268
|
+
import { dirname as dirname26, join as join50, relative as relative9 } from "path";
|
|
8942
9269
|
var FrameworkUpdater = class {
|
|
8943
9270
|
constructor(options = {}) {
|
|
8944
9271
|
this.options = options;
|
|
8945
9272
|
}
|
|
8946
9273
|
async run(projectRoot) {
|
|
8947
|
-
const previousVersion = readText(
|
|
9274
|
+
const previousVersion = readText(join50(projectRoot, PATHS.FRAMEWORK_VERSION));
|
|
8948
9275
|
const manifest = readManifest(projectRoot);
|
|
8949
9276
|
const artifactPolicy = new Map(
|
|
8950
9277
|
manifest?.generated_artifacts.map((artifact) => [artifact.path, artifact.auto_update]) ?? []
|
|
@@ -8954,18 +9281,18 @@ var FrameworkUpdater = class {
|
|
|
8954
9281
|
const skipped = [];
|
|
8955
9282
|
const newScripts = [];
|
|
8956
9283
|
for (const candidate of candidates) {
|
|
8957
|
-
const target =
|
|
9284
|
+
const target = join50(projectRoot, candidate.path);
|
|
8958
9285
|
const existed = existsSync19(target);
|
|
8959
9286
|
const autoUpdate = artifactPolicy.get(candidate.path) ?? candidate.autoUpdate;
|
|
8960
9287
|
if (existed && autoUpdate === false) {
|
|
8961
9288
|
skipped.push({
|
|
8962
9289
|
path: candidate.path,
|
|
8963
|
-
before:
|
|
9290
|
+
before: readFileSync13(target, "utf8"),
|
|
8964
9291
|
after: candidate.content
|
|
8965
9292
|
});
|
|
8966
9293
|
continue;
|
|
8967
9294
|
}
|
|
8968
|
-
|
|
9295
|
+
mkdirSync7(dirname26(target), { recursive: true });
|
|
8969
9296
|
writeFileSync6(target, candidate.content);
|
|
8970
9297
|
if (candidate.executable === true) {
|
|
8971
9298
|
chmodSync3(target, 493);
|
|
@@ -8975,9 +9302,13 @@ var FrameworkUpdater = class {
|
|
|
8975
9302
|
newScripts.push(candidate.path);
|
|
8976
9303
|
}
|
|
8977
9304
|
}
|
|
8978
|
-
|
|
8979
|
-
writeFileSync6(
|
|
8980
|
-
|
|
9305
|
+
mkdirSync7(dirname26(join50(projectRoot, PATHS.FRAMEWORK_VERSION)), { recursive: true });
|
|
9306
|
+
writeFileSync6(
|
|
9307
|
+
join50(projectRoot, PATHS.FRAMEWORK_VERSION),
|
|
9308
|
+
`version=${VERSION}
|
|
9309
|
+
updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
9310
|
+
`
|
|
9311
|
+
);
|
|
8981
9312
|
return {
|
|
8982
9313
|
previous_version: previousVersion,
|
|
8983
9314
|
target_version: VERSION,
|
|
@@ -8996,7 +9327,7 @@ var FrameworkUpdater = class {
|
|
|
8996
9327
|
if (profile === null) {
|
|
8997
9328
|
throw new Error("Cannot update framework-managed artifacts without a project profile");
|
|
8998
9329
|
}
|
|
8999
|
-
const tempRoot = await mkdtemp(
|
|
9330
|
+
const tempRoot = await mkdtemp(join50(tmpdir(), "paqad-ai-update-"));
|
|
9000
9331
|
try {
|
|
9001
9332
|
const result = await new OnboardingOrchestrator().run({
|
|
9002
9333
|
projectRoot: tempRoot,
|
|
@@ -9016,11 +9347,11 @@ var FrameworkUpdater = class {
|
|
|
9016
9347
|
}
|
|
9017
9348
|
};
|
|
9018
9349
|
function readManifest(projectRoot) {
|
|
9019
|
-
const path =
|
|
9350
|
+
const path = join50(projectRoot, PATHS.ONBOARDING_MANIFEST);
|
|
9020
9351
|
if (!existsSync19(path)) {
|
|
9021
9352
|
return null;
|
|
9022
9353
|
}
|
|
9023
|
-
return JSON.parse(
|
|
9354
|
+
return JSON.parse(readFileSync13(path, "utf8"));
|
|
9024
9355
|
}
|
|
9025
9356
|
function readProfile(projectRoot) {
|
|
9026
9357
|
return readProjectProfile(projectRoot);
|
|
@@ -9029,14 +9360,16 @@ function readText(path) {
|
|
|
9029
9360
|
if (!existsSync19(path)) {
|
|
9030
9361
|
return null;
|
|
9031
9362
|
}
|
|
9032
|
-
|
|
9363
|
+
const raw = readFileSync13(path, "utf8");
|
|
9364
|
+
const match = raw.match(/^version=(.+)$/m);
|
|
9365
|
+
return match ? match[1].trim() : raw.trim();
|
|
9033
9366
|
}
|
|
9034
9367
|
async function collectFiles(root, generated) {
|
|
9035
9368
|
const paths = generated.length > 0 ? generated : await walk3(root);
|
|
9036
9369
|
return Promise.all(
|
|
9037
9370
|
paths.map(async (file) => ({
|
|
9038
9371
|
path: file,
|
|
9039
|
-
content: await readFile23(
|
|
9372
|
+
content: await readFile23(join50(root, file), "utf8"),
|
|
9040
9373
|
autoUpdate: true,
|
|
9041
9374
|
executable: file.startsWith("scripts/")
|
|
9042
9375
|
}))
|
|
@@ -9046,7 +9379,7 @@ async function walk3(root, current = root) {
|
|
|
9046
9379
|
const entries = await readdir6(current, { withFileTypes: true });
|
|
9047
9380
|
const files = [];
|
|
9048
9381
|
for (const entry of entries) {
|
|
9049
|
-
const absolute =
|
|
9382
|
+
const absolute = join50(current, entry.name);
|
|
9050
9383
|
if (entry.isDirectory()) {
|
|
9051
9384
|
files.push(...await walk3(root, absolute));
|
|
9052
9385
|
continue;
|
|
@@ -9058,30 +9391,30 @@ async function walk3(root, current = root) {
|
|
|
9058
9391
|
|
|
9059
9392
|
// src/verification/gates/documentation-freshness.ts
|
|
9060
9393
|
import { existsSync as existsSync20 } from "fs";
|
|
9061
|
-
import { join as
|
|
9394
|
+
import { join as join51 } from "path";
|
|
9062
9395
|
var STALENESS_WINDOW_MS2 = 1e3 * 60 * 60 * 24 * 7;
|
|
9063
9396
|
|
|
9064
9397
|
// src/patterns/pattern-store.ts
|
|
9065
9398
|
import { readFile as readFile24, writeFile as writeFile21, mkdir as mkdir22, unlink } from "fs/promises";
|
|
9066
|
-
import { join as
|
|
9399
|
+
import { join as join52, dirname as dirname27 } from "path";
|
|
9067
9400
|
import { homedir as homedir3 } from "os";
|
|
9068
|
-
var GLOBAL_PATTERNS_DIR =
|
|
9401
|
+
var GLOBAL_PATTERNS_DIR = join52(homedir3(), ".paqad", "patterns");
|
|
9069
9402
|
var PatternStore = class {
|
|
9070
9403
|
get indexPath() {
|
|
9071
|
-
return
|
|
9404
|
+
return join52(GLOBAL_PATTERNS_DIR, "index.json");
|
|
9072
9405
|
}
|
|
9073
9406
|
get entriesDir() {
|
|
9074
|
-
return
|
|
9407
|
+
return join52(GLOBAL_PATTERNS_DIR, "entries");
|
|
9075
9408
|
}
|
|
9076
9409
|
async save(pattern) {
|
|
9077
9410
|
await mkdir22(this.entriesDir, { recursive: true });
|
|
9078
|
-
const entryPath =
|
|
9411
|
+
const entryPath = join52(this.entriesDir, `${pattern.id}.json`);
|
|
9079
9412
|
await writeFile21(entryPath, JSON.stringify(pattern, null, 2), "utf8");
|
|
9080
9413
|
await this.updateIndex(pattern);
|
|
9081
9414
|
}
|
|
9082
9415
|
async load(id) {
|
|
9083
9416
|
try {
|
|
9084
|
-
const raw = await readFile24(
|
|
9417
|
+
const raw = await readFile24(join52(this.entriesDir, `${id}.json`), "utf8");
|
|
9085
9418
|
return JSON.parse(raw);
|
|
9086
9419
|
} catch {
|
|
9087
9420
|
return null;
|
|
@@ -9111,12 +9444,12 @@ var PatternStore = class {
|
|
|
9111
9444
|
} else {
|
|
9112
9445
|
index.entries.push(entry);
|
|
9113
9446
|
}
|
|
9114
|
-
await mkdir22(
|
|
9447
|
+
await mkdir22(dirname27(this.indexPath), { recursive: true });
|
|
9115
9448
|
await writeFile21(this.indexPath, JSON.stringify(index, null, 2), "utf8");
|
|
9116
9449
|
}
|
|
9117
9450
|
async delete(id) {
|
|
9118
9451
|
try {
|
|
9119
|
-
await unlink(
|
|
9452
|
+
await unlink(join52(this.entriesDir, `${id}.json`));
|
|
9120
9453
|
} catch {
|
|
9121
9454
|
}
|
|
9122
9455
|
const index = await this.loadIndex();
|
|
@@ -9216,16 +9549,16 @@ ${p.solution}
|
|
|
9216
9549
|
|
|
9217
9550
|
// src/workflows/template-loader.ts
|
|
9218
9551
|
import { readFile as readFile25, readdir as readdir7 } from "fs/promises";
|
|
9219
|
-
import { join as
|
|
9552
|
+
import { join as join53 } from "path";
|
|
9220
9553
|
import YAML6 from "yaml";
|
|
9221
9554
|
|
|
9222
9555
|
// src/workflows/engine.ts
|
|
9223
9556
|
import { readFile as readFile26, writeFile as writeFile23, mkdir as mkdir23 } from "fs/promises";
|
|
9224
|
-
import { join as
|
|
9557
|
+
import { join as join54, dirname as dirname28 } from "path";
|
|
9225
9558
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
9226
9559
|
|
|
9227
9560
|
// src/index.ts
|
|
9228
|
-
var VERSION = "0.1.
|
|
9561
|
+
var VERSION = "0.1.3";
|
|
9229
9562
|
|
|
9230
9563
|
// src/cli/commands/capabilities.ts
|
|
9231
9564
|
import { Command } from "commander";
|
|
@@ -9401,8 +9734,8 @@ function createPacksCommand() {
|
|
|
9401
9734
|
|
|
9402
9735
|
// src/cli/commands/refresh.ts
|
|
9403
9736
|
import { Command as Command7 } from "commander";
|
|
9404
|
-
import { readFileSync as
|
|
9405
|
-
import { join as
|
|
9737
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
9738
|
+
import { join as join55 } from "path";
|
|
9406
9739
|
function createRefreshCommand() {
|
|
9407
9740
|
return new Command7("refresh").description("Refresh derived framework artifacts").option("--project-root <path>", "Project root", process.cwd()).option("--design-system", "Refresh design-system markdown from design tokens").option("--stack", "Refresh the cached stack snapshot").action(async (options) => {
|
|
9408
9741
|
const shouldRefreshDesignSystem = options.designSystem ?? true;
|
|
@@ -9438,14 +9771,14 @@ function createRefreshCommand() {
|
|
|
9438
9771
|
});
|
|
9439
9772
|
}
|
|
9440
9773
|
function writeRefreshDrift(projectRoot, refreshDrift) {
|
|
9441
|
-
const path =
|
|
9774
|
+
const path = join55(projectRoot, PATHS.STACK_DRIFT);
|
|
9442
9775
|
const current = readExistingJson(path);
|
|
9443
9776
|
writeFileSync7(path, `${JSON.stringify({ ...current ?? {}, ...refreshDrift }, null, 2)}
|
|
9444
9777
|
`);
|
|
9445
9778
|
}
|
|
9446
9779
|
function readExistingJson(path) {
|
|
9447
9780
|
try {
|
|
9448
|
-
return JSON.parse(
|
|
9781
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
9449
9782
|
} catch {
|
|
9450
9783
|
return null;
|
|
9451
9784
|
}
|
|
@@ -9454,9 +9787,25 @@ function readExistingJson(path) {
|
|
|
9454
9787
|
// src/cli/commands/update.ts
|
|
9455
9788
|
import { Command as Command8 } from "commander";
|
|
9456
9789
|
function createUpdateCommand() {
|
|
9457
|
-
return new Command8("update").description("Update framework-managed artifacts").option("--project-root <path>", "Project root", process.cwd()).action(async (options) => {
|
|
9458
|
-
|
|
9459
|
-
|
|
9790
|
+
return new Command8("update").description("Update framework-managed artifacts").option("--project-root <path>", "Project root", process.cwd()).option("--silent", "Suppress output; write results to audit log only").action(async (options) => {
|
|
9791
|
+
try {
|
|
9792
|
+
const report = await new FrameworkUpdater().run(options.projectRoot);
|
|
9793
|
+
if (!options.silent) {
|
|
9794
|
+
console.log(JSON.stringify(report, null, 2));
|
|
9795
|
+
}
|
|
9796
|
+
appendAuditLog2(options.projectRoot, report.previous_version, report.target_version);
|
|
9797
|
+
} catch (err) {
|
|
9798
|
+
if (!options.silent) {
|
|
9799
|
+
throw err;
|
|
9800
|
+
}
|
|
9801
|
+
appendAuditLogFailure(
|
|
9802
|
+
options.projectRoot,
|
|
9803
|
+
null,
|
|
9804
|
+
VERSION,
|
|
9805
|
+
err instanceof Error ? err.message : String(err)
|
|
9806
|
+
);
|
|
9807
|
+
process.exit(0);
|
|
9808
|
+
}
|
|
9460
9809
|
});
|
|
9461
9810
|
}
|
|
9462
9811
|
|