0sec-cli 0.22.1 → 0.22.4
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/0.js +56 -45
- package/README.md +12 -0
- package/chunks/{adapt-loop-J6QCZZTB.js → adapt-loop-3CCOWE43.js} +4 -4
- package/chunks/{artifact-scraper-MFKT6OI4.js → artifact-scraper-Z4UUIOH3.js} +2 -2
- package/chunks/{assumption-mining-646LYSJY.js → assumption-mining-2UAKQB6V.js} +18 -14
- package/chunks/{chunk-OHHCGDJ2.js → chunk-2WWAPKAW.js} +126 -1089
- package/chunks/{chunk-XDKB2P5X.js → chunk-3BPX75OR.js} +1 -1
- package/chunks/chunk-4ZYYX5GY.js +74 -0
- package/chunks/{chunk-3E2KALZ5.js → chunk-5JNTMWPK.js} +2 -2
- package/chunks/{chunk-CPWA6RWE.js → chunk-5WAJGLCW.js} +3 -3
- package/chunks/{chunk-KAU6NLGP.js → chunk-6KFB6AYN.js} +515 -20
- package/chunks/{chunk-XA2O4WXA.js → chunk-6WQDXWQC.js} +169 -15
- package/chunks/chunk-6YZE552K.js +5603 -0
- package/chunks/chunk-BGX53365.js +309 -0
- package/chunks/{chunk-X7LAEEUW.js → chunk-C6JPA32H.js} +1 -1
- package/chunks/chunk-CF3MNS3O.js +990 -0
- package/chunks/{chunk-4VGLO2YS.js → chunk-CPCHLL6A.js} +5 -2
- package/chunks/{chunk-ZKQ2I3S2.js → chunk-DPUXXMJW.js} +1 -1
- package/chunks/{chunk-XCISCI4T.js → chunk-E3OOBVOK.js} +30 -12
- package/chunks/{chunk-4G7OVYJX.js → chunk-E74LXQFY.js} +1 -1
- package/chunks/{chunk-OSBPGWTE.js → chunk-ELI3BVIK.js} +3 -3
- package/chunks/chunk-FFHRANYC.js +547 -0
- package/chunks/{chunk-WSDDGFA6.js → chunk-G2BE2FC7.js} +1 -1
- package/chunks/chunk-GQ3DGHWE.js +1659 -0
- package/chunks/{chunk-V36HVTUL.js → chunk-GVY3S5E5.js} +21 -18
- package/chunks/{chunk-5BTJMINS.js → chunk-H4DH7COV.js} +192 -40
- package/chunks/{chunk-HHPWRY2M.js → chunk-H6Q2QAIY.js} +23475 -22747
- package/chunks/{chunk-7XCKYNLO.js → chunk-IHPTOTLL.js} +1 -1
- package/chunks/chunk-IXAHQICR.js +31 -0
- package/chunks/{chunk-TH2LW437.js → chunk-KCA35IL3.js} +2 -190
- package/chunks/{chunk-VWITLWXB.js → chunk-L2CCO4KE.js} +282 -34
- package/chunks/{chunk-6YNKRUMC.js → chunk-NJLKFC66.js} +321 -5
- package/chunks/{chunk-W6UWZFKL.js → chunk-O6KPVYOI.js} +100 -5
- package/chunks/{chunk-LGCASF2C.js → chunk-OAS5SAVZ.js} +6767 -2100
- package/chunks/chunk-PS5E7LUQ.js +277 -0
- package/chunks/{chunk-AV2LG2L7.js → chunk-QVIRWJ45.js} +257 -145
- package/chunks/{chunk-TV27OWIW.js → chunk-QWK4R3DB.js} +9 -9
- package/chunks/{chunk-WZRVS7SB.js → chunk-RCRTSKQN.js} +1 -1
- package/chunks/chunk-RS4MAN3Q.js +73 -0
- package/chunks/{chunk-V6GHX34G.js → chunk-RTBZ36BD.js} +8 -274
- package/chunks/{chunk-YL6YAGLL.js → chunk-SCWWLABR.js} +1 -1
- package/chunks/chunk-VDOWARMJ.js +63 -0
- package/chunks/{chunk-IFME676H.js → chunk-WBRGXCU2.js} +79 -18
- package/chunks/{chunk-IVQV574Y.js → chunk-WHUAUFSX.js} +1 -1
- package/chunks/{chunk-GOEA3NW6.js → chunk-XODDLNBI.js} +1534 -1305
- package/chunks/chunk-YAZARMJY.js +355 -0
- package/chunks/{chunk-NLH4LMWH.js → chunk-YED25PA4.js} +1 -1
- package/chunks/{codex-models-NHQW45SO.js → codex-models-Z43V3427.js} +9 -3
- package/chunks/{commands-N6GN5SBF.js → commands-EUGF4FUZ.js} +5842 -4093
- package/chunks/{db-5IWEJUYV.js → db-NVAW4WWR.js} +3 -3
- package/chunks/{dist-L3XJWWZ6.js → dist-27BNG6WD.js} +193 -112
- package/chunks/{dist-FSS7MBDF.js → dist-7RUOKPRJ.js} +12 -2
- package/chunks/{dist-KEULE5IQ.js → dist-LNXGATR2.js} +2 -2
- package/chunks/{dist-XZB53PS5.js → dist-M2X367VW.js} +2 -2
- package/chunks/{dist-BS4CY4RG.js → dist-NG2HT5XU.js} +27 -1
- package/chunks/{eval-runner-EMEGF24P.js → eval-runner-6Q3H52SP.js} +13 -9
- package/chunks/{exploit-agent-KW4EB7BE.js → exploit-agent-FCCR2HYX.js} +1 -1
- package/chunks/{exploit-autoclimb-CXJ2ERRO.js → exploit-autoclimb-X5ZS5FAO.js} +1 -1
- package/chunks/{exploit-climb-TJWLY4XC.js → exploit-climb-3JHGEO36.js} +2 -2
- package/chunks/{fix-UUKPXCFB.js → fix-NSNPU33B.js} +12 -6
- package/chunks/{harness-C5HOKIEV.js → harness-KXQOVIDQ.js} +3 -3
- package/chunks/{hunt-scan-KZZ444V2.js → hunt-scan-ELTKGKC4.js} +16 -12
- package/chunks/{kernel-primitive-Z77IR7YS.js → kernel-primitive-MT7YZSIG.js} +2 -2
- package/chunks/{kernel-vm-runner-JW77P276.js → kernel-vm-runner-YFRHYC6G.js} +2 -2
- package/chunks/{native-loop-P6P5GYVJ.js → native-loop-OFJDRHPI.js} +18 -8
- package/chunks/orchestrate-DRZ2SVCP.js +62 -0
- package/chunks/{prepare-KYHGFWCJ.js → prepare-72GWTRVI.js} +6 -2
- package/chunks/{process-N245QIQG.js → process-MJYJ2K5E.js} +2 -1
- package/chunks/{run-GAFKBCTN.js → run-WTJCZZHV.js} +1901 -4816
- package/chunks/{runtime-H7YBR5IZ.js → runtime-FCKOUB7V.js} +10 -4
- package/chunks/{runtime-XFWJTBUO.js → runtime-LCJ4XSKE.js} +5 -1
- package/chunks/{scan-stream-BMOCPKQX.js → scan-stream-GT6GKQ3U.js} +7 -1
- package/chunks/{scope-QWY4VQZ7.js → scope-E5QZUOOH.js} +2 -2
- package/chunks/service-plugins-MSK2EQP6.js +62 -0
- package/chunks/{session-store-XIDFGG7K.js → session-store-H3Q7KCYG.js} +6 -4
- package/chunks/{variant-candidates-7D4F5E4H.js → variant-candidates-SZKDCOLK.js} +9 -3
- package/chunks/{web-recon-prepass-VXXBPDAV.js → web-recon-prepass-IO3GXQMW.js} +11 -6
- package/dashboard/apple-touch-icon.png +0 -0
- package/dashboard/assets/operations-BvB5Qpmk.css +1 -0
- package/dashboard/assets/operations-D8Ii01SV.js +114 -0
- package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
- package/dashboard/favicon.ico +0 -0
- package/dashboard/favicon.svg +10 -0
- package/dashboard/index.html +5 -3
- package/package.json +1 -1
- package/chunks/chunk-6GHSR47F.js +0 -147
- package/chunks/chunk-BCSRRQG5.js +0 -3514
- package/chunks/chunk-BOUGKEKA.js +0 -193
- package/chunks/chunk-GHWODZMR.js +0 -16
- package/chunks/orchestrate-DWWUT5RC.js +0 -58
- package/dashboard/assets/bot-Bg1Tp9_d.js +0 -1
- package/dashboard/assets/dist-B_dDCPbu.js +0 -1
- package/dashboard/assets/findings-page-mayGhNx6.js +0 -5
- package/dashboard/assets/format--susmHX7.js +0 -1
- package/dashboard/assets/input-TuVPR3Zl.js +0 -1
- package/dashboard/assets/jsx-runtime-MGseXjj5.js +0 -3
- package/dashboard/assets/live-page-HWWHX99U.js +0 -1
- package/dashboard/assets/meta-tile-ckHo-w7k.js +0 -1
- package/dashboard/assets/operations-BxZ0OoXO.js +0 -10
- package/dashboard/assets/operations-CMFVrXEe.css +0 -2
- package/dashboard/assets/operations-app-D3d35rgo.js +0 -46
- package/dashboard/assets/overview-page-W_4IteEr.js +0 -1
- package/dashboard/assets/page-header-Bw9AJgBV.js +0 -1
- package/dashboard/assets/scans-page-CgzVXHnn.js +0 -1
- package/dashboard/assets/siren-KQj8QAnJ.js +0 -1
- package/dashboard/assets/table-8EDklulN.js +0 -1
- package/dashboard/assets/tabs-CFHPOwg8.js +0 -1
|
@@ -3,8 +3,9 @@ import { createRequire as __0CreateRequire } from "node:module";
|
|
|
3
3
|
const require = __0CreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
homeStateDir,
|
|
6
|
+
parseSecurityWorkflowInput,
|
|
6
7
|
runStateDir
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6KFB6AYN.js";
|
|
8
9
|
import {
|
|
9
10
|
__export
|
|
10
11
|
} from "./chunk-VCSFCHAJ.js";
|
|
@@ -229,11 +230,22 @@ function createDrizzleFromShim(client, config = {}) {
|
|
|
229
230
|
return db;
|
|
230
231
|
}
|
|
231
232
|
|
|
233
|
+
// packages/db/dist/db-directory.js
|
|
234
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
235
|
+
import { dirname, resolve } from "node:path";
|
|
236
|
+
function ensureDatabaseDirectory(path) {
|
|
237
|
+
if (path === ":memory:")
|
|
238
|
+
return;
|
|
239
|
+
const directory = dirname(resolve(path));
|
|
240
|
+
if (!existsSync(directory))
|
|
241
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
242
|
+
}
|
|
243
|
+
|
|
232
244
|
// packages/db/dist/database.js
|
|
233
245
|
import { asc, eq, desc, and, gt, inArray, or } from "drizzle-orm";
|
|
234
246
|
import { createHash, randomUUID } from "node:crypto";
|
|
235
|
-
import {
|
|
236
|
-
import { closeSync, existsSync
|
|
247
|
+
import { join } from "node:path";
|
|
248
|
+
import { closeSync, existsSync as existsSync2, openSync, readSync, renameSync, rmSync, statSync, writeSync } from "node:fs";
|
|
237
249
|
|
|
238
250
|
// packages/db/dist/schema.js
|
|
239
251
|
var schema_exports = {};
|
|
@@ -622,12 +634,10 @@ function resolveOsecDbPath(dbPath) {
|
|
|
622
634
|
}
|
|
623
635
|
function resetOsecDatabase(dbPath) {
|
|
624
636
|
const path = resolveOsecDbPath(dbPath);
|
|
625
|
-
|
|
626
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
627
|
-
}
|
|
637
|
+
ensureDatabaseDirectory(path);
|
|
628
638
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
629
639
|
const candidate = `${path}${suffix}`;
|
|
630
|
-
if (
|
|
640
|
+
if (existsSync2(candidate)) {
|
|
631
641
|
rmSync(candidate, { force: true });
|
|
632
642
|
}
|
|
633
643
|
}
|
|
@@ -635,9 +645,7 @@ function resetOsecDatabase(dbPath) {
|
|
|
635
645
|
}
|
|
636
646
|
function repairOsecDatabase(dbPath) {
|
|
637
647
|
const path = resolveOsecDbPath(dbPath);
|
|
638
|
-
|
|
639
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
640
|
-
}
|
|
648
|
+
ensureDatabaseDirectory(path);
|
|
641
649
|
clearStaleLockIfAny(path);
|
|
642
650
|
const backupPath = backupCorruptDatabase(path) ?? void 0;
|
|
643
651
|
resetOsecDatabase(path);
|
|
@@ -653,13 +661,13 @@ function timestampTag() {
|
|
|
653
661
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
654
662
|
}
|
|
655
663
|
function backupCorruptDatabase(path) {
|
|
656
|
-
if (!
|
|
664
|
+
if (!existsSync2(path))
|
|
657
665
|
return null;
|
|
658
666
|
const backupPath = `${path}.corrupt-${timestampTag()}`;
|
|
659
667
|
renameSync(path, backupPath);
|
|
660
668
|
for (const suffix of ["-wal", "-shm", ".lock"]) {
|
|
661
669
|
const candidate = `${path}${suffix}`;
|
|
662
|
-
if (!
|
|
670
|
+
if (!existsSync2(candidate))
|
|
663
671
|
continue;
|
|
664
672
|
try {
|
|
665
673
|
renameSync(candidate, `${backupPath}${suffix}`);
|
|
@@ -679,7 +687,7 @@ function ensureDatabaseHealthy(sqlite) {
|
|
|
679
687
|
}
|
|
680
688
|
}
|
|
681
689
|
function migrateWalHeaderIfNeeded(path) {
|
|
682
|
-
if (!
|
|
690
|
+
if (!existsSync2(path))
|
|
683
691
|
return;
|
|
684
692
|
let size;
|
|
685
693
|
try {
|
|
@@ -708,7 +716,7 @@ function migrateWalHeaderIfNeeded(path) {
|
|
|
708
716
|
const shmPath = `${path}-shm`;
|
|
709
717
|
let hadWalData = false;
|
|
710
718
|
try {
|
|
711
|
-
if (
|
|
719
|
+
if (existsSync2(walPath) && statSync(walPath).size > 0)
|
|
712
720
|
hadWalData = true;
|
|
713
721
|
} catch {
|
|
714
722
|
}
|
|
@@ -723,7 +731,7 @@ function migrateWalHeaderIfNeeded(path) {
|
|
|
723
731
|
return;
|
|
724
732
|
}
|
|
725
733
|
for (const sidecar of [walPath, shmPath]) {
|
|
726
|
-
if (
|
|
734
|
+
if (existsSync2(sidecar)) {
|
|
727
735
|
try {
|
|
728
736
|
rmSync(sidecar, { force: true });
|
|
729
737
|
} catch {
|
|
@@ -760,15 +768,13 @@ var osecDB = class {
|
|
|
760
768
|
constructor(dbPath, options = {}) {
|
|
761
769
|
const path = resolveOsecDbPath(dbPath);
|
|
762
770
|
if (options.readOnly) {
|
|
763
|
-
if (path !== ":memory:" && !
|
|
771
|
+
if (path !== ":memory:" && !existsSync2(path)) {
|
|
764
772
|
throw new Error(`Database does not exist: ${path}`);
|
|
765
773
|
}
|
|
766
774
|
this.initializeReadOnlyDatabase(path);
|
|
767
775
|
return;
|
|
768
776
|
}
|
|
769
|
-
|
|
770
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
771
|
-
}
|
|
777
|
+
ensureDatabaseDirectory(path);
|
|
772
778
|
this.openWithRecovery(path);
|
|
773
779
|
}
|
|
774
780
|
openWithRecovery(path) {
|
|
@@ -1954,8 +1960,8 @@ var osecDB = class {
|
|
|
1954
1960
|
});
|
|
1955
1961
|
return result.changes;
|
|
1956
1962
|
}
|
|
1957
|
-
deleteWorkersByStatus(
|
|
1958
|
-
const values = Array.isArray(
|
|
1963
|
+
deleteWorkersByStatus(statuses2) {
|
|
1964
|
+
const values = Array.isArray(statuses2) ? statuses2 : [statuses2];
|
|
1959
1965
|
if (values.length === 0)
|
|
1960
1966
|
return 0;
|
|
1961
1967
|
const placeholders = values.map(() => "?").join(",");
|
|
@@ -2526,8 +2532,8 @@ function normalizeWorkflowStatus(value, fallback) {
|
|
|
2526
2532
|
|
|
2527
2533
|
// packages/db/dist/run-storage.js
|
|
2528
2534
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2529
|
-
import { chmodSync, existsSync as
|
|
2530
|
-
import { dirname as dirname2, isAbsolute, join as join2, resolve } from "node:path";
|
|
2535
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync2, rmSync as rmSync2, writeFileSync } from "node:fs";
|
|
2536
|
+
import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2 } from "node:path";
|
|
2531
2537
|
var STATE_DB_FILE = "state.db";
|
|
2532
2538
|
var REPORT_FILE = "report.json";
|
|
2533
2539
|
function normalizeRunId(value) {
|
|
@@ -2543,10 +2549,10 @@ function resolveOsecRunStorage(options = {}) {
|
|
|
2543
2549
|
const stateDir = homeStateDir(options.homeDir);
|
|
2544
2550
|
const configuredRunDir = env["ZERO_RUN_DIR"]?.trim() || void 0;
|
|
2545
2551
|
const configuredDbPath = options.dbPath ?? (env["ZERO_DB_PATH"]?.trim() || void 0);
|
|
2546
|
-
let runDir = configuredRunDir ?
|
|
2547
|
-
if (options.resume && !configuredRunDir && !configuredDbPath && !
|
|
2552
|
+
let runDir = configuredRunDir ? resolve2(configuredRunDir) : runStateDir(runId, options.homeDir);
|
|
2553
|
+
if (options.resume && !configuredRunDir && !configuredDbPath && !existsSync3(runDir)) {
|
|
2548
2554
|
const runsDir = join2(stateDir, "runs");
|
|
2549
|
-
if (
|
|
2555
|
+
if (existsSync3(runsDir)) {
|
|
2550
2556
|
const matches = readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(runId));
|
|
2551
2557
|
if (matches.length > 1) {
|
|
2552
2558
|
throw new Error(`0 run id prefix '${runId}' is ambiguous.`);
|
|
@@ -2561,12 +2567,12 @@ function resolveOsecRunStorage(options = {}) {
|
|
|
2561
2567
|
if (!isAbsolute(runDir)) {
|
|
2562
2568
|
throw new Error(`0 run directory must be absolute: ${JSON.stringify(runDir)}.`);
|
|
2563
2569
|
}
|
|
2564
|
-
let dbPath = configuredDbPath ? configuredDbPath === ":memory:" ? configuredDbPath :
|
|
2570
|
+
let dbPath = configuredDbPath ? configuredDbPath === ":memory:" ? configuredDbPath : resolve2(configuredDbPath) : join2(runDir, STATE_DB_FILE);
|
|
2565
2571
|
const configuredReportPath = env["ZERO_REPORT_PATH"]?.trim() || void 0;
|
|
2566
|
-
const reportPath = configuredReportPath ?
|
|
2567
|
-
if (options.resume && !configuredDbPath && !
|
|
2572
|
+
const reportPath = configuredReportPath ? resolve2(configuredReportPath) : !configuredDbPath || configuredRunDir ? join2(runDir, REPORT_FILE) : void 0;
|
|
2573
|
+
if (options.resume && !configuredDbPath && !existsSync3(dbPath)) {
|
|
2568
2574
|
const legacyDbPath = join2(stateDir, "0.db");
|
|
2569
|
-
if (
|
|
2575
|
+
if (existsSync3(legacyDbPath))
|
|
2570
2576
|
dbPath = legacyDbPath;
|
|
2571
2577
|
}
|
|
2572
2578
|
if (!configuredDbPath || configuredRunDir || reportPath) {
|
|
@@ -2587,9 +2593,9 @@ function resolveOsecRunStorage(options = {}) {
|
|
|
2587
2593
|
}
|
|
2588
2594
|
function listOsecRunDatabasePaths(homeDir) {
|
|
2589
2595
|
const runsDir = join2(homeStateDir(homeDir), "runs");
|
|
2590
|
-
if (!
|
|
2596
|
+
if (!existsSync3(runsDir))
|
|
2591
2597
|
return [];
|
|
2592
|
-
return readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join2(runsDir, entry.name, STATE_DB_FILE)).filter((path) =>
|
|
2598
|
+
return readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join2(runsDir, entry.name, STATE_DB_FILE)).filter((path) => existsSync3(path));
|
|
2593
2599
|
}
|
|
2594
2600
|
function writeOsecRunReport(storage, report) {
|
|
2595
2601
|
const reportPath = storage.reportPath;
|
|
@@ -2603,12 +2609,249 @@ function writeOsecRunReport(storage, report) {
|
|
|
2603
2609
|
renameSync2(temporaryPath, reportPath);
|
|
2604
2610
|
chmodSync(reportPath, 384);
|
|
2605
2611
|
} finally {
|
|
2606
|
-
if (
|
|
2612
|
+
if (existsSync3(temporaryPath)) {
|
|
2607
2613
|
rmSync2(temporaryPath, { force: true });
|
|
2608
2614
|
}
|
|
2609
2615
|
}
|
|
2610
2616
|
}
|
|
2611
2617
|
|
|
2618
|
+
// packages/db/dist/security-workflows.js
|
|
2619
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
2620
|
+
var SecurityWorkflowStoreError = class extends Error {
|
|
2621
|
+
statusCode;
|
|
2622
|
+
constructor(message, statusCode) {
|
|
2623
|
+
super(message);
|
|
2624
|
+
this.statusCode = statusCode;
|
|
2625
|
+
this.name = "SecurityWorkflowStoreError";
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
var activeStatuses = { queued: true, running: true };
|
|
2629
|
+
var statuses = { queued: true, running: true, completed: true, failed: true, cancelled: true, interrupted: true };
|
|
2630
|
+
var SecurityWorkflowStore = class {
|
|
2631
|
+
#db;
|
|
2632
|
+
#runnerInstanceId = randomUUID3();
|
|
2633
|
+
constructor(dbPath) {
|
|
2634
|
+
const path = resolveOsecDbPath(dbPath);
|
|
2635
|
+
ensureDatabaseDirectory(path);
|
|
2636
|
+
this.#db = createShimmedDatabase(path);
|
|
2637
|
+
this.#db.pragma("busy_timeout = 5000");
|
|
2638
|
+
this.#db.exec(`CREATE TABLE IF NOT EXISTS workflow_definitions (id TEXT PRIMARY KEY, revision INTEGER NOT NULL, definition_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
|
|
2639
|
+
CREATE TABLE IF NOT EXISTS workflow_executions (id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL, workflow_revision INTEGER NOT NULL, session_id TEXT NOT NULL, status TEXT NOT NULL, execution_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
|
|
2640
|
+
CREATE INDEX IF NOT EXISTS workflow_executions_workflow ON workflow_executions(workflow_id, created_at);
|
|
2641
|
+
CREATE INDEX IF NOT EXISTS workflow_executions_session ON workflow_executions(session_id, created_at);`);
|
|
2642
|
+
}
|
|
2643
|
+
list() {
|
|
2644
|
+
return this.#db.prepare("SELECT definition_json FROM workflow_definitions ORDER BY updated_at DESC, id").all().map((row) => JSON.parse(String(row.definition_json)));
|
|
2645
|
+
}
|
|
2646
|
+
get(id) {
|
|
2647
|
+
const row = this.#db.prepare("SELECT definition_json FROM workflow_definitions WHERE id = ?").all(id)[0];
|
|
2648
|
+
return row ? JSON.parse(String(row.definition_json)) : null;
|
|
2649
|
+
}
|
|
2650
|
+
save(value) {
|
|
2651
|
+
let input;
|
|
2652
|
+
try {
|
|
2653
|
+
input = parseSecurityWorkflowInput(value);
|
|
2654
|
+
} catch (error) {
|
|
2655
|
+
throw new SecurityWorkflowStoreError(error instanceof Error ? error.message : "Invalid workflow.", 400);
|
|
2656
|
+
}
|
|
2657
|
+
return this.#db.transaction(() => {
|
|
2658
|
+
const existing = input.id ? this.get(input.id) : null;
|
|
2659
|
+
if (input.id && !existing && input.revision !== void 0)
|
|
2660
|
+
throw new SecurityWorkflowStoreError("Workflow not found.", 404);
|
|
2661
|
+
if (existing && input.revision !== existing.revision)
|
|
2662
|
+
throw new SecurityWorkflowStoreError("Workflow changed. Reload before saving.", 409);
|
|
2663
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2664
|
+
const workflow = { ...input, id: input.id ?? randomUUID3(), revision: (existing?.revision ?? 0) + 1, createdAt: existing?.createdAt ?? now, updatedAt: now };
|
|
2665
|
+
if (existing)
|
|
2666
|
+
this.#db.prepare("UPDATE workflow_definitions SET revision = ?, definition_json = ?, updated_at = ? WHERE id = ?").run(workflow.revision, JSON.stringify(workflow), now, workflow.id);
|
|
2667
|
+
else
|
|
2668
|
+
this.#db.prepare("INSERT INTO workflow_definitions (id, revision, definition_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(workflow.id, workflow.revision, JSON.stringify(workflow), now, now);
|
|
2669
|
+
return workflow;
|
|
2670
|
+
})();
|
|
2671
|
+
}
|
|
2672
|
+
delete(id, revision) {
|
|
2673
|
+
return this.#db.transaction(() => {
|
|
2674
|
+
const workflow = this.get(id);
|
|
2675
|
+
if (!workflow)
|
|
2676
|
+
return false;
|
|
2677
|
+
if (revision !== void 0 && revision !== workflow.revision)
|
|
2678
|
+
throw new SecurityWorkflowStoreError("Workflow changed. Reload before deleting.", 409);
|
|
2679
|
+
this.#db.prepare("DELETE FROM workflow_definitions WHERE id = ?").run(id);
|
|
2680
|
+
return true;
|
|
2681
|
+
})();
|
|
2682
|
+
}
|
|
2683
|
+
createExecution(workflowId, sessionId, revision) {
|
|
2684
|
+
if (!sessionId || sessionId.length > 128)
|
|
2685
|
+
throw new SecurityWorkflowStoreError("Invalid execution session.", 400);
|
|
2686
|
+
return this.#db.transaction(() => {
|
|
2687
|
+
const workflow = this.get(workflowId);
|
|
2688
|
+
if (!workflow)
|
|
2689
|
+
throw new SecurityWorkflowStoreError("Workflow not found.", 404);
|
|
2690
|
+
if (revision !== void 0 && revision !== workflow.revision)
|
|
2691
|
+
throw new SecurityWorkflowStoreError("Workflow changed. Review it before running.", 409);
|
|
2692
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2693
|
+
const execution = { id: randomUUID3(), workflowId, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
|
|
2694
|
+
this.#db.prepare("INSERT INTO workflow_executions (id, workflow_id, workflow_revision, session_id, status, execution_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(execution.id, workflowId, workflow.revision, sessionId, execution.status, JSON.stringify(execution), now, now);
|
|
2695
|
+
return execution;
|
|
2696
|
+
})();
|
|
2697
|
+
}
|
|
2698
|
+
getExecution(id) {
|
|
2699
|
+
const row = this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE id = ?").all(id)[0];
|
|
2700
|
+
return row ? JSON.parse(String(row.execution_json)) : null;
|
|
2701
|
+
}
|
|
2702
|
+
listExecutions(workflowId, sessionId) {
|
|
2703
|
+
return this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE (? IS NULL OR workflow_id = ?) AND (? IS NULL OR session_id = ?) ORDER BY created_at DESC, id LIMIT 200").all(workflowId ?? null, workflowId ?? null, sessionId ?? null, sessionId ?? null).map((row) => JSON.parse(String(row.execution_json)));
|
|
2704
|
+
}
|
|
2705
|
+
updateExecution(id, update) {
|
|
2706
|
+
return this.#db.transaction(() => {
|
|
2707
|
+
const current = this.getExecution(id);
|
|
2708
|
+
if (!current)
|
|
2709
|
+
throw new SecurityWorkflowStoreError("Execution not found.", 404);
|
|
2710
|
+
if (!activeStatuses[current.status])
|
|
2711
|
+
throw new SecurityWorkflowStoreError("Execution is already finished.", 409);
|
|
2712
|
+
if (update.status !== void 0 && (!Object.hasOwn(statuses, update.status) || current.status === "running" && update.status === "queued"))
|
|
2713
|
+
throw new SecurityWorkflowStoreError("Invalid execution status transition.", 400);
|
|
2714
|
+
if (update.jobId !== void 0 && (typeof update.jobId !== "string" || update.jobId.length > 128))
|
|
2715
|
+
throw new SecurityWorkflowStoreError("Invalid execution job ID.", 400);
|
|
2716
|
+
if (update.error !== void 0 && (typeof update.error !== "string" || update.error.length > 16e3))
|
|
2717
|
+
throw new SecurityWorkflowStoreError("Invalid execution error.", 400);
|
|
2718
|
+
if (update.nodeResults !== void 0) {
|
|
2719
|
+
const nodeIds = new Set(current.workflow.nodes.map((node) => node.id));
|
|
2720
|
+
if (!update.nodeResults || typeof update.nodeResults !== "object" || Array.isArray(update.nodeResults) || JSON.stringify(update.nodeResults).length > 1e5 || Object.entries(update.nodeResults).some(([key, result]) => {
|
|
2721
|
+
if (!nodeIds.has(key) || !result || typeof result !== "object" || Array.isArray(result) || !Object.hasOwn(result, "status") || typeof result.status !== "string" || !result.status.length || result.status.length > 64)
|
|
2722
|
+
return true;
|
|
2723
|
+
if (Object.keys(result).some((field) => !["status", "jobId", "scanId", "scanIds", "dbPaths", "error"].includes(field)))
|
|
2724
|
+
return true;
|
|
2725
|
+
if ([result.jobId, result.scanId].some((value) => value !== void 0 && (typeof value !== "string" || value.length > 128)))
|
|
2726
|
+
return true;
|
|
2727
|
+
if (result.error !== void 0 && (typeof result.error !== "string" || result.error.length > 16e3))
|
|
2728
|
+
return true;
|
|
2729
|
+
return [result.scanIds, result.dbPaths].some((values) => values !== void 0 && (!Array.isArray(values) || values.length > 64 || values.some((value) => typeof value !== "string" || value.length > 4096)));
|
|
2730
|
+
}))
|
|
2731
|
+
throw new SecurityWorkflowStoreError("Invalid execution node results.", 400);
|
|
2732
|
+
}
|
|
2733
|
+
const execution = { ...current, status: update.status ?? current.status, jobId: update.jobId ?? current.jobId, nodeResults: update.nodeResults ?? current.nodeResults, error: update.error ?? current.error, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2734
|
+
this.#db.prepare("UPDATE workflow_executions SET status = ?, execution_json = ?, updated_at = ? WHERE id = ?").run(execution.status, JSON.stringify(execution), execution.updatedAt, id);
|
|
2735
|
+
return execution;
|
|
2736
|
+
})();
|
|
2737
|
+
}
|
|
2738
|
+
interruptActiveExecutions(isOwnerAlive = processAlive) {
|
|
2739
|
+
const rows = this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE status IN ('queued', 'running')").all();
|
|
2740
|
+
let interrupted = 0;
|
|
2741
|
+
for (const row of rows) {
|
|
2742
|
+
const execution = JSON.parse(String(row.execution_json));
|
|
2743
|
+
if (!Number.isInteger(execution.ownerPid) || execution.ownerPid <= 0 || isOwnerAlive(execution.ownerPid))
|
|
2744
|
+
continue;
|
|
2745
|
+
try {
|
|
2746
|
+
this.updateExecution(execution.id, { status: "interrupted", error: "The local engine stopped. Run this workflow again to continue." });
|
|
2747
|
+
interrupted++;
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
if (!(error instanceof SecurityWorkflowStoreError && error.statusCode === 409))
|
|
2750
|
+
throw error;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return interrupted;
|
|
2754
|
+
}
|
|
2755
|
+
close() {
|
|
2756
|
+
this.#db.close();
|
|
2757
|
+
}
|
|
2758
|
+
};
|
|
2759
|
+
function processAlive(pid) {
|
|
2760
|
+
try {
|
|
2761
|
+
process.kill(pid, 0);
|
|
2762
|
+
return true;
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// packages/db/dist/workflow-triggers.js
|
|
2769
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
2770
|
+
var TRIGGER_INTERVAL_MS = { hourly: 36e5, daily: 864e5, weekly: 6048e5 };
|
|
2771
|
+
function nextWorkflowTriggerFire(startAt, cadence, now) {
|
|
2772
|
+
const anchor = Date.parse(startAt);
|
|
2773
|
+
const interval = TRIGGER_INTERVAL_MS[cadence];
|
|
2774
|
+
if (!Number.isFinite(anchor) || !interval || !Number.isFinite(now))
|
|
2775
|
+
throw new Error("Invalid schedule.");
|
|
2776
|
+
return new Date(anchor > now ? anchor : anchor + (Math.floor((now - anchor) / interval) + 1) * interval).toISOString();
|
|
2777
|
+
}
|
|
2778
|
+
var WorkflowTriggerStore = class {
|
|
2779
|
+
#db;
|
|
2780
|
+
constructor(dbPath) {
|
|
2781
|
+
const path = resolveOsecDbPath(dbPath);
|
|
2782
|
+
ensureDatabaseDirectory(path);
|
|
2783
|
+
this.#db = createShimmedDatabase(path);
|
|
2784
|
+
this.#db.pragma("busy_timeout = 5000");
|
|
2785
|
+
this.#db.exec("CREATE TABLE IF NOT EXISTS workflow_triggers (id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL, enabled INTEGER NOT NULL, next_fire_at TEXT NOT NULL, active_claim TEXT, trigger_json TEXT NOT NULL)");
|
|
2786
|
+
this.#db.exec("CREATE INDEX IF NOT EXISTS workflow_triggers_due ON workflow_triggers(enabled, next_fire_at)");
|
|
2787
|
+
}
|
|
2788
|
+
get(id) {
|
|
2789
|
+
const row = this.#db.prepare("SELECT trigger_json FROM workflow_triggers WHERE id = ?").all(id)[0];
|
|
2790
|
+
return row ? JSON.parse(String(row.trigger_json)) : null;
|
|
2791
|
+
}
|
|
2792
|
+
list(workflowId) {
|
|
2793
|
+
return this.#db.prepare("SELECT trigger_json FROM workflow_triggers WHERE (? IS NULL OR workflow_id = ?) ORDER BY next_fire_at, id LIMIT 200").all(workflowId ?? null, workflowId ?? null).map((row) => JSON.parse(String(row.trigger_json)));
|
|
2794
|
+
}
|
|
2795
|
+
create(input, now = Date.now()) {
|
|
2796
|
+
const stamp = new Date(now).toISOString();
|
|
2797
|
+
const trigger = { ...input, kind: "schedule", id: randomUUID4(), version: 1, nextFireAt: nextWorkflowTriggerFire(input.startAt, input.cadence, now), createdAt: stamp, updatedAt: stamp };
|
|
2798
|
+
this.#db.prepare("INSERT INTO workflow_triggers(id,workflow_id,enabled,next_fire_at,active_claim,trigger_json) VALUES(?,?,?,?,NULL,?)").run(trigger.id, trigger.workflowId, trigger.enabled ? 1 : 0, trigger.nextFireAt, JSON.stringify(trigger));
|
|
2799
|
+
return trigger;
|
|
2800
|
+
}
|
|
2801
|
+
update(id, update, expectedVersion) {
|
|
2802
|
+
return this.#db.transaction(() => {
|
|
2803
|
+
const current = this.get(id);
|
|
2804
|
+
if (!current)
|
|
2805
|
+
return null;
|
|
2806
|
+
if (expectedVersion !== void 0 && (current.version ?? 1) !== expectedVersion)
|
|
2807
|
+
throw new Error("Schedule changed. Review it again before enabling.");
|
|
2808
|
+
const next = { ...current, ...update, version: (current.version ?? 1) + 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2809
|
+
if (!next.enabled && update.lastStatus === "running")
|
|
2810
|
+
next.lastStatus = "paused";
|
|
2811
|
+
this.#write(next);
|
|
2812
|
+
return next;
|
|
2813
|
+
})();
|
|
2814
|
+
}
|
|
2815
|
+
delete(id) {
|
|
2816
|
+
return this.#db.prepare("DELETE FROM workflow_triggers WHERE id = ? AND active_claim IS NULL").run(id).changes > 0;
|
|
2817
|
+
}
|
|
2818
|
+
/** CAS claims the occurrence across engines; advancing from now skips missed intervals. */
|
|
2819
|
+
claim(id, now = Date.now()) {
|
|
2820
|
+
const current = this.get(id);
|
|
2821
|
+
if (!current?.enabled || current.activeClaim || Date.parse(current.nextFireAt) > now)
|
|
2822
|
+
return null;
|
|
2823
|
+
const claim = randomUUID4();
|
|
2824
|
+
const next = { ...current, version: (current.version ?? 1) + 1, activeClaim: claim, ownerPid: process.pid, nextFireAt: nextWorkflowTriggerFire(current.startAt, current.cadence, now), lastStatus: "starting", lastExecutionId: void 0, lastError: void 0, updatedAt: new Date(now).toISOString() };
|
|
2825
|
+
this.#db.prepare("UPDATE workflow_triggers SET active_claim=?, next_fire_at=?, trigger_json=? WHERE id=? AND enabled=1 AND active_claim IS NULL AND next_fire_at=? AND trigger_json=?").run(claim, next.nextFireAt, JSON.stringify(next), id, current.nextFireAt, JSON.stringify(current));
|
|
2826
|
+
return this.get(id)?.activeClaim === claim ? next : null;
|
|
2827
|
+
}
|
|
2828
|
+
release(id, claim, update) {
|
|
2829
|
+
this.#db.exec("BEGIN IMMEDIATE");
|
|
2830
|
+
try {
|
|
2831
|
+
const current = this.get(id);
|
|
2832
|
+
if (!current || current.activeClaim !== claim) {
|
|
2833
|
+
this.#db.exec("COMMIT");
|
|
2834
|
+
return null;
|
|
2835
|
+
}
|
|
2836
|
+
const next = { ...current, ...update, version: (current.version ?? 1) + 1, enabled: current.enabled && update.enabled !== false, activeClaim: void 0, ownerPid: void 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2837
|
+
if (!current.enabled && update.enabled !== false)
|
|
2838
|
+
next.lastStatus = "paused";
|
|
2839
|
+
this.#db.prepare("UPDATE workflow_triggers SET active_claim=NULL,enabled=?,trigger_json=? WHERE id=? AND active_claim=?").run(next.enabled ? 1 : 0, JSON.stringify(next), id, claim);
|
|
2840
|
+
this.#db.exec("COMMIT");
|
|
2841
|
+
return next;
|
|
2842
|
+
} catch (error) {
|
|
2843
|
+
this.#db.exec("ROLLBACK");
|
|
2844
|
+
throw error;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
#write(trigger) {
|
|
2848
|
+
this.#db.prepare("UPDATE workflow_triggers SET enabled=?,next_fire_at=?,trigger_json=? WHERE id=?").run(trigger.enabled ? 1 : 0, trigger.nextFireAt, JSON.stringify(trigger), trigger.id);
|
|
2849
|
+
}
|
|
2850
|
+
close() {
|
|
2851
|
+
this.#db.close();
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
|
|
2612
2855
|
export {
|
|
2613
2856
|
findingStatuses,
|
|
2614
2857
|
findingTriageStatuses,
|
|
@@ -2641,5 +2884,10 @@ export {
|
|
|
2641
2884
|
restoreFindingReviewFields,
|
|
2642
2885
|
resolveOsecRunStorage,
|
|
2643
2886
|
listOsecRunDatabasePaths,
|
|
2644
|
-
writeOsecRunReport
|
|
2887
|
+
writeOsecRunReport,
|
|
2888
|
+
SecurityWorkflowStoreError,
|
|
2889
|
+
SecurityWorkflowStore,
|
|
2890
|
+
TRIGGER_INTERVAL_MS,
|
|
2891
|
+
nextWorkflowTriggerFire,
|
|
2892
|
+
WorkflowTriggerStore
|
|
2645
2893
|
};
|