sentinelayer-cli 0.3.0 → 0.4.5
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/README.md +7 -5
- package/package.json +4 -3
- package/src/agents/jules/pulse.js +14 -6
- package/src/agents/jules/tools/auth-audit.js +410 -79
- package/src/agents/jules/tools/runtime-audit.js +36 -26
- package/src/agents/jules/tools/url-policy.js +100 -0
- package/src/auth/gate.js +45 -11
- package/src/auth/http.js +204 -47
- package/src/cli.js +1 -1
- package/src/legacy-cli.js +68 -24
- package/src/review/local-review.js +11 -0
- package/src/scaffold/templates.js +1 -1
- package/src/telemetry/sync.js +12 -3
package/src/legacy-cli.js
CHANGED
|
@@ -143,7 +143,7 @@ function parseCliArgs(argv) {
|
|
|
143
143
|
for (let i = 0; i < argv.length; i += 1) {
|
|
144
144
|
const arg = String(argv[i] || "").trim();
|
|
145
145
|
if (!arg) continue;
|
|
146
|
-
if (arg === "--help" || arg === "-h") {
|
|
146
|
+
if (arg === "--help" || arg === "-h" || arg === "help") {
|
|
147
147
|
showHelp = true;
|
|
148
148
|
continue;
|
|
149
149
|
}
|
|
@@ -191,32 +191,66 @@ function parseCliArgs(argv) {
|
|
|
191
191
|
function printUsage() {
|
|
192
192
|
console.log(`sentinelayer-cli v${CLI_VERSION}`);
|
|
193
193
|
console.log("");
|
|
194
|
-
console.log("Usage:");
|
|
195
|
-
console.log("
|
|
196
|
-
console.log("
|
|
197
|
-
console.log("
|
|
198
|
-
console.log("
|
|
199
|
-
console.log("
|
|
200
|
-
console.log("
|
|
201
|
-
console.log("
|
|
202
|
-
console.log("
|
|
203
|
-
console.log("
|
|
194
|
+
console.log("Usage: sl <command> [options]");
|
|
195
|
+
console.log("");
|
|
196
|
+
console.log("Scaffold:");
|
|
197
|
+
console.log(" sl [project-name] Create a new project with SentinelLayer scaffolding");
|
|
198
|
+
console.log(" sl init [project-name] Same as above (interactive or --non-interactive)");
|
|
199
|
+
console.log("");
|
|
200
|
+
console.log("Authentication:");
|
|
201
|
+
console.log(" sl auth login Log in via browser (provisions SentinelLayer + AIdenID)");
|
|
202
|
+
console.log(" sl auth status Show authentication and AIdenID provisioning status");
|
|
203
|
+
console.log(" sl auth sessions List stored session metadata");
|
|
204
|
+
console.log(" sl auth logout Clear local session");
|
|
205
|
+
console.log("");
|
|
206
|
+
console.log("Security & Review:");
|
|
207
|
+
console.log(" sl review scan --path . --json Deterministic code review (full or --mode diff)");
|
|
208
|
+
console.log(" sl /omargate deep --path . --json Local Omar Gate security scan (P0/P1/P2 findings)");
|
|
209
|
+
console.log(" sl scan init Generate .github/workflows/omar-gate.yml from spec");
|
|
210
|
+
console.log(" sl scan setup-secrets --repo <slug> Inject SENTINELAYER_TOKEN into GitHub repo secrets");
|
|
211
|
+
console.log("");
|
|
212
|
+
console.log("Specification & Planning:");
|
|
213
|
+
console.log(" sl spec list-templates List available project templates");
|
|
214
|
+
console.log(" sl spec generate Generate SPEC.md from template or AI");
|
|
215
|
+
console.log(" sl prompt generate Generate agent execution prompt from spec");
|
|
216
|
+
console.log(" sl guide generate Generate BUILD_GUIDE.md from spec");
|
|
217
|
+
console.log(" sl ingest map --json Codebase AST ingest with framework detection");
|
|
218
|
+
console.log("");
|
|
219
|
+
console.log("Audit & Quality:");
|
|
220
|
+
console.log(" sl audit --path . --json Full 15-agent audit swarm");
|
|
221
|
+
console.log(" sl audit frontend --path . --json Jules frontend audit (--stream for NDJSON, --url for runtime)");
|
|
222
|
+
console.log(" sl audit security --path . --json Security-focused audit");
|
|
223
|
+
console.log("");
|
|
224
|
+
console.log("AIdenID (Identity Testing):");
|
|
225
|
+
console.log(" sl ai identity provision --execute Provision ephemeral test email (auto-credentials after login)");
|
|
226
|
+
console.log(" sl ai identity wait-for-otp <id> Poll for OTP extraction from provisioned email");
|
|
227
|
+
console.log(" sl ai identity list List tracked identities");
|
|
228
|
+
console.log(" sl ai identity lineage <id> Show identity parent/child tree");
|
|
229
|
+
console.log(" sl ai identity revoke <id> Revoke a provisioned identity");
|
|
230
|
+
console.log("");
|
|
231
|
+
console.log("Cost & Policy:");
|
|
232
|
+
console.log(" sl cost show --json Show accumulated cost tracking");
|
|
233
|
+
console.log(" sl policy list List available policy packs");
|
|
234
|
+
console.log(" sl policy use <pack> Switch active policy pack");
|
|
235
|
+
console.log("");
|
|
236
|
+
console.log("Advanced:");
|
|
237
|
+
console.log(" sl swarm plan --path . --json Multi-agent swarm planning");
|
|
238
|
+
console.log(" sl mcp list --json List MCP registries and adapters");
|
|
239
|
+
console.log(" sl telemetry show --json Show run event ledger");
|
|
240
|
+
console.log(" sl config list Show current configuration");
|
|
204
241
|
console.log("");
|
|
205
242
|
console.log("Options:");
|
|
206
|
-
console.log(" -h, --help Show help");
|
|
243
|
+
console.log(" -h, --help Show this help");
|
|
207
244
|
console.log(" -v, --version Show CLI version");
|
|
208
|
-
console.log(" --
|
|
209
|
-
console.log(" --
|
|
210
|
-
console.log(" --
|
|
211
|
-
console.log(" --path PATH Target path for local command mode");
|
|
212
|
-
console.log(" --output-dir PATH Artifact root for local command reports (default .sentinelayer)");
|
|
213
|
-
console.log(" --json Emit machine-readable JSON for local command mode");
|
|
245
|
+
console.log(" --json Machine-readable JSON output");
|
|
246
|
+
console.log(" --path PATH Target workspace path");
|
|
247
|
+
console.log(" --non-interactive Disable prompts (require --interview-file)");
|
|
214
248
|
console.log("");
|
|
215
|
-
console.log("
|
|
216
|
-
console.log("
|
|
217
|
-
console.log("
|
|
218
|
-
console.log("
|
|
219
|
-
console.log("
|
|
249
|
+
console.log("Quickstart:");
|
|
250
|
+
console.log(" sl auth login && npx create-sentinelayer my-app && cd my-app");
|
|
251
|
+
console.log(" # Then hand docs/spec.md to your coding agent");
|
|
252
|
+
console.log("");
|
|
253
|
+
console.log("Docs: https://sentinelayer.com/docs");
|
|
220
254
|
}
|
|
221
255
|
|
|
222
256
|
function normalizeInterviewInput(
|
|
@@ -808,6 +842,12 @@ async function collectScanFiles(rootPath) {
|
|
|
808
842
|
}
|
|
809
843
|
|
|
810
844
|
async function runCredentialScan(targetPath) {
|
|
845
|
+
const testOrFixturePathPattern = /(?:^|[\\/])(?:test|tests|__tests__|fixtures?)(?:[\\/]|$)/i;
|
|
846
|
+
const localReviewSourcePathPattern = /(?:^|[\\/])src[\\/]review[\\/]local-review\.js$/i;
|
|
847
|
+
const workItemExcludePathPattern = new RegExp(
|
|
848
|
+
`${testOrFixturePathPattern.source}|${localReviewSourcePathPattern.source}`,
|
|
849
|
+
"i"
|
|
850
|
+
);
|
|
811
851
|
const rules = [
|
|
812
852
|
{
|
|
813
853
|
severity: "P1",
|
|
@@ -828,11 +868,13 @@ async function runCredentialScan(targetPath) {
|
|
|
828
868
|
severity: "P2",
|
|
829
869
|
message: "Possible hardcoded credential literal.",
|
|
830
870
|
regex: /(api[_-]?key|secret|token)\s*[:=]\s*['"][^'"]{20,}['"]/i,
|
|
871
|
+
excludePathPattern: testOrFixturePathPattern,
|
|
831
872
|
},
|
|
832
873
|
{
|
|
833
874
|
severity: "P2",
|
|
834
875
|
message: "Work-item marker found.",
|
|
835
876
|
regex: /\b(?:\x54\x4f\x44\x4f|\x46\x49\x58\x4d\x45|\x48\x41\x43\x4b)\b/,
|
|
877
|
+
excludePathPattern: workItemExcludePathPattern,
|
|
836
878
|
},
|
|
837
879
|
];
|
|
838
880
|
|
|
@@ -841,6 +883,7 @@ async function runCredentialScan(targetPath) {
|
|
|
841
883
|
const maxFindings = 200;
|
|
842
884
|
|
|
843
885
|
for (const filePath of files) {
|
|
886
|
+
const relativePath = path.relative(targetPath, filePath).replace(/\\/g, "/");
|
|
844
887
|
let text = "";
|
|
845
888
|
try {
|
|
846
889
|
text = await fsp.readFile(filePath, "utf-8");
|
|
@@ -853,10 +896,11 @@ async function runCredentialScan(targetPath) {
|
|
|
853
896
|
if (!line) continue;
|
|
854
897
|
if (line.includes("<your-token>") || line.includes("example")) continue;
|
|
855
898
|
for (const rule of rules) {
|
|
899
|
+
if (rule.excludePathPattern && rule.excludePathPattern.test(relativePath)) continue;
|
|
856
900
|
if (!rule.regex.test(line)) continue;
|
|
857
901
|
findings.push({
|
|
858
902
|
severity: rule.severity,
|
|
859
|
-
file:
|
|
903
|
+
file: relativePath,
|
|
860
904
|
line: lineIndex + 1,
|
|
861
905
|
message: rule.message,
|
|
862
906
|
excerpt: line.trim().slice(0, 180),
|
|
@@ -45,6 +45,13 @@ const SEVERITY_ORDER = new Map([
|
|
|
45
45
|
["P3", 3],
|
|
46
46
|
]);
|
|
47
47
|
|
|
48
|
+
const TEST_OR_FIXTURE_PATH_PATTERN = /(?:^|[\\/])(?:test|tests|__tests__|fixtures?)(?:[\\/]|$)/i;
|
|
49
|
+
const LOCAL_REVIEW_SOURCE_PATH_PATTERN = /(?:^|[\\/])src[\\/]review[\\/]local-review\.js$/i;
|
|
50
|
+
const WORK_ITEM_MARKER_EXCLUDE_PATH_PATTERN = new RegExp(
|
|
51
|
+
`${TEST_OR_FIXTURE_PATH_PATTERN.source}|${LOCAL_REVIEW_SOURCE_PATH_PATTERN.source}`,
|
|
52
|
+
"i"
|
|
53
|
+
);
|
|
54
|
+
|
|
48
55
|
const REVIEW_RULES = Object.freeze([
|
|
49
56
|
{
|
|
50
57
|
severity: "P1",
|
|
@@ -65,11 +72,13 @@ const REVIEW_RULES = Object.freeze([
|
|
|
65
72
|
severity: "P2",
|
|
66
73
|
message: "Possible hardcoded credential literal.",
|
|
67
74
|
regex: /(api[_-]?key|secret|token)\s*[:=]\s*['\"][^'\"]{20,}['\"]/i,
|
|
75
|
+
excludePathPattern: TEST_OR_FIXTURE_PATH_PATTERN,
|
|
68
76
|
},
|
|
69
77
|
{
|
|
70
78
|
severity: "P2",
|
|
71
79
|
message: "Work-item marker found.",
|
|
72
80
|
regex: /\b(?:\x54\x4f\x44\x4f|\x46\x49\x58\x4d\x45|\x48\x41\x43\x4b)\b/,
|
|
81
|
+
excludePathPattern: WORK_ITEM_MARKER_EXCLUDE_PATH_PATTERN,
|
|
73
82
|
},
|
|
74
83
|
]);
|
|
75
84
|
|
|
@@ -112,6 +121,7 @@ const DETERMINISTIC_REVIEW_RULES = Object.freeze([
|
|
|
112
121
|
suggestedFix: "Replace literal with environment/secret-manager lookup.",
|
|
113
122
|
regex: /(api[_-]?key|secret|token|password|passwd)\s*[:=]\s*['\"][^'\"]{12,}['\"]/i,
|
|
114
123
|
sourceOnly: true,
|
|
124
|
+
excludePathPattern: TEST_OR_FIXTURE_PATH_PATTERN,
|
|
115
125
|
},
|
|
116
126
|
{
|
|
117
127
|
id: "SL-SEC-006",
|
|
@@ -184,6 +194,7 @@ const DETERMINISTIC_REVIEW_RULES = Object.freeze([
|
|
|
184
194
|
suggestedFix: "Resolve or scope pending work before release.",
|
|
185
195
|
regex: /\b(?:TODO|FIXME|HACK)\b/,
|
|
186
196
|
sourceOnly: true,
|
|
197
|
+
excludePathPattern: WORK_ITEM_MARKER_EXCLUDE_PATH_PATTERN,
|
|
187
198
|
},
|
|
188
199
|
{
|
|
189
200
|
id: "SL-SEC-015",
|
package/src/telemetry/sync.js
CHANGED
|
@@ -117,15 +117,14 @@ export async function syncRunToDashboard(runData) {
|
|
|
117
117
|
},
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
const response = await
|
|
120
|
+
const response = await fetchWithTimeout(apiUrl + "/api/v1/telemetry", {
|
|
121
121
|
method: "POST",
|
|
122
122
|
headers: {
|
|
123
123
|
"Content-Type": "application/json",
|
|
124
124
|
Authorization: "Bearer " + session.token,
|
|
125
125
|
},
|
|
126
|
-
signal: AbortSignal.timeout(SYNC_TIMEOUT_MS),
|
|
127
126
|
body: JSON.stringify(payload),
|
|
128
|
-
});
|
|
127
|
+
}, SYNC_TIMEOUT_MS);
|
|
129
128
|
|
|
130
129
|
if (!response.ok) {
|
|
131
130
|
consecutiveFailures++;
|
|
@@ -188,3 +187,13 @@ function detectGitRef() {
|
|
|
188
187
|
return "main";
|
|
189
188
|
}
|
|
190
189
|
}
|
|
190
|
+
|
|
191
|
+
async function fetchWithTimeout(url, options, timeoutMs) {
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
194
|
+
try {
|
|
195
|
+
return await fetch(url, { ...options, signal: controller.signal });
|
|
196
|
+
} finally {
|
|
197
|
+
clearTimeout(timeoutHandle);
|
|
198
|
+
}
|
|
199
|
+
}
|