sentinelayer-cli 0.34.4 → 0.34.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/package.json +1 -1
- package/src/commands/session.js +19 -8
- package/src/legacy-cli.js +26 -5
- package/src/review/local-review.js +14 -0
- package/src/scan/generator.js +26 -5
- package/src/session/control-events.js +17 -0
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -5579,6 +5579,9 @@ export function registerSessionCommand(program) {
|
|
|
5579
5579
|
const rawActionEvents = remoteActions?.ok
|
|
5580
5580
|
? buildSessionActionEvents(normalizedSessionId, remoteActions.actions)
|
|
5581
5581
|
: [];
|
|
5582
|
+
const visibleActionEvents = includeControlEvents
|
|
5583
|
+
? rawActionEvents
|
|
5584
|
+
: rawActionEvents.filter((event) => !isSessionControlEvent(event));
|
|
5582
5585
|
const unacknowledgedHumanMessages = remoteActions?.ok
|
|
5583
5586
|
? readProjectionUnacknowledgedHumanMessages(remoteActions)
|
|
5584
5587
|
: [];
|
|
@@ -5598,8 +5601,8 @@ export function registerSessionCommand(program) {
|
|
|
5598
5601
|
: sourceEvents.filter((event) => !isSessionControlEvent(event));
|
|
5599
5602
|
const hiddenControlEventCount = sourceEvents.length - transcriptEvents.length;
|
|
5600
5603
|
const actionEvents = remoteWindowRead
|
|
5601
|
-
? filterSessionActionEventsForTranscriptWindow(
|
|
5602
|
-
:
|
|
5604
|
+
? filterSessionActionEventsForTranscriptWindow(visibleActionEvents, transcriptEvents)
|
|
5605
|
+
: visibleActionEvents;
|
|
5603
5606
|
const events = mergeSessionActionEvents(transcriptEvents, actionEvents).slice(-tail);
|
|
5604
5607
|
const autoView = recordSessionReadViews(normalizedSessionId, events, {
|
|
5605
5608
|
targetPath,
|
|
@@ -6250,15 +6253,19 @@ export function registerSessionCommand(program) {
|
|
|
6250
6253
|
limit: 5_000,
|
|
6251
6254
|
}),
|
|
6252
6255
|
]);
|
|
6253
|
-
const
|
|
6256
|
+
const includeControlEvents = Boolean(options.includeControlEvents);
|
|
6257
|
+
const rawActionEvents = remoteActions?.ok
|
|
6254
6258
|
? buildSessionActionEvents(normalizedSessionId, remoteActions.actions)
|
|
6255
6259
|
: [];
|
|
6256
|
-
const
|
|
6260
|
+
const actionEvents = includeControlEvents
|
|
6261
|
+
? rawActionEvents
|
|
6262
|
+
: rawActionEvents.filter((event) => !isSessionControlEvent(event));
|
|
6257
6263
|
const dedupedEvents = dedupeSessionEvents(events);
|
|
6258
6264
|
const transcriptBaseEvents = includeControlEvents
|
|
6259
6265
|
? dedupedEvents
|
|
6260
6266
|
: filterSessionMaterialEvents(dedupedEvents);
|
|
6261
|
-
const hiddenControlEventCount =
|
|
6267
|
+
const hiddenControlEventCount =
|
|
6268
|
+
dedupedEvents.length - transcriptBaseEvents.length + rawActionEvents.length - actionEvents.length;
|
|
6262
6269
|
const exportEvents = mergeSessionActionEvents(transcriptBaseEvents, actionEvents);
|
|
6263
6270
|
const stats = computeTranscriptStats({
|
|
6264
6271
|
sessionMeta: sessionPayload,
|
|
@@ -6405,15 +6412,19 @@ export function registerSessionCommand(program) {
|
|
|
6405
6412
|
listAgents(normalizedSessionId, { targetPath, includeInactive: true }),
|
|
6406
6413
|
readStream(normalizedSessionId, { targetPath, tail: 0 }),
|
|
6407
6414
|
]);
|
|
6408
|
-
const
|
|
6415
|
+
const includeControlEvents = Boolean(options.includeControlEvents);
|
|
6416
|
+
const rawActionEvents = remoteActions?.ok
|
|
6409
6417
|
? buildSessionActionEvents(normalizedSessionId, remoteActions.actions)
|
|
6410
6418
|
: [];
|
|
6411
|
-
const
|
|
6419
|
+
const actionEvents = includeControlEvents
|
|
6420
|
+
? rawActionEvents
|
|
6421
|
+
: rawActionEvents.filter((event) => !isSessionControlEvent(event));
|
|
6412
6422
|
const dedupedEvents = dedupeSessionEvents(events);
|
|
6413
6423
|
const transcriptBaseEvents = includeControlEvents
|
|
6414
6424
|
? dedupedEvents
|
|
6415
6425
|
: filterSessionMaterialEvents(dedupedEvents);
|
|
6416
|
-
const hiddenControlEventCount =
|
|
6426
|
+
const hiddenControlEventCount =
|
|
6427
|
+
dedupedEvents.length - transcriptBaseEvents.length + rawActionEvents.length - actionEvents.length;
|
|
6417
6428
|
const transcriptEvents = mergeSessionActionEvents(transcriptBaseEvents, actionEvents);
|
|
6418
6429
|
|
|
6419
6430
|
// Pull GitHub/Google avatar + display name from the active auth
|
package/src/legacy-cli.js
CHANGED
|
@@ -2520,16 +2520,22 @@ jobs:
|
|
|
2520
2520
|
uses: mrrCarter/sentinelayer-v1-action@03d7369cba7de2e9f15b959275c982111f0ee493
|
|
2521
2521
|
with:
|
|
2522
2522
|
github_token: \${{ github.token }}
|
|
2523
|
+
openai_api_key: \${{ secrets.OPENAI_API_KEY }}
|
|
2524
|
+
google_api_key: \${{ secrets.GOOGLE_API_KEY }}
|
|
2525
|
+
llm_provider: \${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}
|
|
2523
2526
|
sentinelayer_token: \${{ secrets.${normalizedSecret} }}${specIdBindingLine}
|
|
2524
|
-
sentinelayer_managed_llm:
|
|
2527
|
+
sentinelayer_managed_llm: \${{ secrets.GOOGLE_API_KEY == '' && secrets.OPENAI_API_KEY == '' && secrets.${normalizedSecret} != '' }}
|
|
2525
2528
|
scan_mode: \${{ github.event_name == 'workflow_dispatch' && inputs.scan_mode || 'deep' }}
|
|
2526
2529
|
severity_gate: \${{ github.event_name == 'workflow_dispatch' && inputs.severity_gate || 'P1' }}
|
|
2527
|
-
model: gpt-5.3-codex
|
|
2530
|
+
model: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-pro' || 'gpt-5.3-codex' }}
|
|
2528
2531
|
codex_model: gpt-5.3-codex
|
|
2529
|
-
model_fallback: gpt-4.1-mini
|
|
2530
|
-
use_codex:
|
|
2532
|
+
model_fallback: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-flash' || 'gpt-4.1-mini' }}
|
|
2533
|
+
use_codex: \${{ secrets.GOOGLE_API_KEY == '' }}
|
|
2531
2534
|
codex_only: "false"
|
|
2532
2535
|
llm_failure_policy: block
|
|
2536
|
+
max_daily_scans: \${{ vars.OMAR_MAX_DAILY_SCANS || '200' }}
|
|
2537
|
+
min_scan_interval_minutes: \${{ vars.OMAR_MIN_SCAN_INTERVAL_MINUTES || '0' }}
|
|
2538
|
+
rate_limit_fail_mode: closed
|
|
2533
2539
|
- name: Enforce Omar reviewer merge thresholds
|
|
2534
2540
|
shell: bash
|
|
2535
2541
|
env:
|
|
@@ -2575,6 +2581,12 @@ jobs:
|
|
|
2575
2581
|
OMAR_SCAN_MODE: \${{ github.event_name == 'workflow_dispatch' && inputs.scan_mode || 'deep' }}
|
|
2576
2582
|
OMAR_SEVERITY_GATE: \${{ github.event_name == 'workflow_dispatch' && inputs.severity_gate || 'P1' }}
|
|
2577
2583
|
OMAR_P2_MAX_ALLOWED: \${{ github.event_name == 'workflow_dispatch' && inputs.p2_max_allowed || '5' }}
|
|
2584
|
+
OMAR_OPENAI_KEY_PRESENT: \${{ secrets.OPENAI_API_KEY != '' }}
|
|
2585
|
+
OMAR_GOOGLE_KEY_PRESENT: \${{ secrets.GOOGLE_API_KEY != '' }}
|
|
2586
|
+
OMAR_MANAGED_LLM: \${{ secrets.GOOGLE_API_KEY == '' && secrets.OPENAI_API_KEY == '' && secrets.${normalizedSecret} != '' }}
|
|
2587
|
+
OMAR_LLM_PROVIDER: \${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}
|
|
2588
|
+
OMAR_MODEL: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-pro' || 'gpt-5.3-codex' }}
|
|
2589
|
+
OMAR_MODEL_FALLBACK: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-flash' || 'gpt-4.1-mini' }}
|
|
2578
2590
|
run: |
|
|
2579
2591
|
set -euo pipefail
|
|
2580
2592
|
mkdir -p omar-artifacts
|
|
@@ -2590,6 +2602,9 @@ jobs:
|
|
|
2590
2602
|
value = env(name, "0").strip()
|
|
2591
2603
|
return int(value) if value.isdigit() else 0
|
|
2592
2604
|
|
|
2605
|
+
def bool_env(name):
|
|
2606
|
+
return env(name).strip().lower() == "true"
|
|
2607
|
+
|
|
2593
2608
|
github_run_id = env("GITHUB_RUN_ID")
|
|
2594
2609
|
server_url = env("GITHUB_SERVER_URL", "https://github.com").rstrip("/")
|
|
2595
2610
|
repository = env("GITHUB_REPOSITORY")
|
|
@@ -2612,7 +2627,13 @@ jobs:
|
|
|
2612
2627
|
"scan": {
|
|
2613
2628
|
"mode": env("OMAR_SCAN_MODE", "deep"),
|
|
2614
2629
|
"action_ref": "mrrCarter/sentinelayer-v1-action@03d7369cba7de2e9f15b959275c982111f0ee493",
|
|
2615
|
-
"
|
|
2630
|
+
"llm_provider": env("OMAR_LLM_PROVIDER", "openai"),
|
|
2631
|
+
"model": env("OMAR_MODEL", "gpt-5.3-codex"),
|
|
2632
|
+
"model_fallback": env("OMAR_MODEL_FALLBACK", "gpt-4.1-mini"),
|
|
2633
|
+
"llm_route": "google_api_key" if bool_env("OMAR_GOOGLE_KEY_PRESENT") else ("openai_api_key" if bool_env("OMAR_OPENAI_KEY_PRESENT") else "sentinelayer_managed"),
|
|
2634
|
+
"google_key_present": bool_env("OMAR_GOOGLE_KEY_PRESENT"),
|
|
2635
|
+
"openai_key_present": bool_env("OMAR_OPENAI_KEY_PRESENT"),
|
|
2636
|
+
"managed_llm": bool_env("OMAR_MANAGED_LLM"),
|
|
2616
2637
|
"llm_failure_policy": "block",
|
|
2617
2638
|
},
|
|
2618
2639
|
"github": {
|
|
@@ -95,6 +95,7 @@ const REVIEW_RULES = Object.freeze([
|
|
|
95
95
|
regex: /\b(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{20,})\b/,
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
|
+
id: "SL-SEC-005",
|
|
98
99
|
severity: "P2",
|
|
99
100
|
message: "Possible hardcoded credential literal.",
|
|
100
101
|
regex: /(api[_-]?key|secret|token)\s*[:=]\s*['\"][^'\"]{20,}['\"]/i,
|
|
@@ -336,6 +337,16 @@ function regexMatches(regex, input) {
|
|
|
336
337
|
return new RegExp(regex.source, flags).test(input);
|
|
337
338
|
}
|
|
338
339
|
|
|
340
|
+
function isGitHubActionsContextExpressionLiteral(line) {
|
|
341
|
+
return /(?:[:=]\s*)['"]?\s*\$\{\{\s*(?:github|secrets|vars|env|inputs|steps|needs|matrix|runner|strategy)\.[^}]+\}\}\s*['"]?/i.test(
|
|
342
|
+
String(line || "")
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function shouldSuppressRuleLine(rule, line) {
|
|
347
|
+
return rule.id === "SL-SEC-005" && isGitHubActionsContextExpressionLiteral(line);
|
|
348
|
+
}
|
|
349
|
+
|
|
339
350
|
function sanitizeLineForExcerpt(line) {
|
|
340
351
|
return String(line || "")
|
|
341
352
|
.trim()
|
|
@@ -590,6 +601,9 @@ async function scanRulesForFiles({ rootPath, filePaths, rules, maxFindings = MAX
|
|
|
590
601
|
if (!regexMatches(rule.regex, line)) {
|
|
591
602
|
continue;
|
|
592
603
|
}
|
|
604
|
+
if (shouldSuppressRuleLine(rule, line)) {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
593
607
|
const pushed = tryPushFinding(
|
|
594
608
|
findings,
|
|
595
609
|
createFinding({
|
package/src/scan/generator.js
CHANGED
|
@@ -226,16 +226,22 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
226
226
|
uses: SENTINELAYER_ACTION_REF,
|
|
227
227
|
with: {
|
|
228
228
|
github_token: "${{ github.token }}",
|
|
229
|
+
openai_api_key: "${{ secrets.OPENAI_API_KEY }}",
|
|
230
|
+
google_api_key: "${{ secrets.GOOGLE_API_KEY }}",
|
|
231
|
+
llm_provider: "${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}",
|
|
229
232
|
sentinelayer_token: `\${{ secrets.${normalizedSecret} }}`,
|
|
230
|
-
sentinelayer_managed_llm:
|
|
233
|
+
sentinelayer_managed_llm: `\${{ secrets.GOOGLE_API_KEY == '' && secrets.OPENAI_API_KEY == '' && secrets.${normalizedSecret} != '' }}`,
|
|
231
234
|
scan_mode: profile.scanMode || "deep",
|
|
232
235
|
severity_gate: profile.severityGate || "P1",
|
|
233
|
-
model: "gpt-5.3-codex",
|
|
236
|
+
model: "${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-pro' || 'gpt-5.3-codex' }}",
|
|
234
237
|
codex_model: "gpt-5.3-codex",
|
|
235
|
-
model_fallback: "gpt-4.1-mini",
|
|
236
|
-
use_codex: "
|
|
238
|
+
model_fallback: "${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-flash' || 'gpt-4.1-mini' }}",
|
|
239
|
+
use_codex: "${{ secrets.GOOGLE_API_KEY == '' }}",
|
|
237
240
|
codex_only: "false",
|
|
238
241
|
llm_failure_policy: "block",
|
|
242
|
+
max_daily_scans: "${{ vars.OMAR_MAX_DAILY_SCANS || '200' }}",
|
|
243
|
+
min_scan_interval_minutes: "${{ vars.OMAR_MIN_SCAN_INTERVAL_MINUTES || '0' }}",
|
|
244
|
+
rate_limit_fail_mode: "closed",
|
|
239
245
|
playwright_mode: profile.playwrightMode || "off",
|
|
240
246
|
sbom_mode: profile.sbomMode || "off",
|
|
241
247
|
wait_for_completion: "true",
|
|
@@ -288,6 +294,12 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
288
294
|
OMAR_SCAN_MODE: "${{ github.event_name == 'workflow_dispatch' && inputs.scan_mode || 'deep' }}",
|
|
289
295
|
OMAR_SEVERITY_GATE: "${{ github.event_name == 'workflow_dispatch' && inputs.severity_gate || 'P1' }}",
|
|
290
296
|
OMAR_P2_MAX_ALLOWED: "${{ github.event_name == 'workflow_dispatch' && inputs.p2_max_allowed || '5' }}",
|
|
297
|
+
OMAR_OPENAI_KEY_PRESENT: "${{ secrets.OPENAI_API_KEY != '' }}",
|
|
298
|
+
OMAR_GOOGLE_KEY_PRESENT: "${{ secrets.GOOGLE_API_KEY != '' }}",
|
|
299
|
+
OMAR_MANAGED_LLM: `\${{ secrets.GOOGLE_API_KEY == '' && secrets.OPENAI_API_KEY == '' && secrets.${normalizedSecret} != '' }}`,
|
|
300
|
+
OMAR_LLM_PROVIDER: "${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}",
|
|
301
|
+
OMAR_MODEL: "${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-pro' || 'gpt-5.3-codex' }}",
|
|
302
|
+
OMAR_MODEL_FALLBACK: "${{ secrets.GOOGLE_API_KEY != '' && 'gemini-2.5-flash' || 'gpt-4.1-mini' }}",
|
|
291
303
|
},
|
|
292
304
|
run: [
|
|
293
305
|
"set -euo pipefail",
|
|
@@ -304,6 +316,9 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
304
316
|
" value = env(name, '0').strip()",
|
|
305
317
|
" return int(value) if value.isdigit() else 0",
|
|
306
318
|
"",
|
|
319
|
+
"def bool_env(name):",
|
|
320
|
+
" return env(name).strip().lower() == 'true'",
|
|
321
|
+
"",
|
|
307
322
|
"github_run_id = env('GITHUB_RUN_ID')",
|
|
308
323
|
"server_url = env('GITHUB_SERVER_URL', 'https://github.com').rstrip('/')",
|
|
309
324
|
"repository = env('GITHUB_REPOSITORY')",
|
|
@@ -326,7 +341,13 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
326
341
|
" 'scan': {",
|
|
327
342
|
" 'mode': env('OMAR_SCAN_MODE', 'deep'),",
|
|
328
343
|
` 'action_ref': '${SENTINELAYER_ACTION_REF}',`,
|
|
329
|
-
" '
|
|
344
|
+
" 'llm_provider': env('OMAR_LLM_PROVIDER', 'openai'),",
|
|
345
|
+
" 'model': env('OMAR_MODEL', 'gpt-5.3-codex'),",
|
|
346
|
+
" 'model_fallback': env('OMAR_MODEL_FALLBACK', 'gpt-4.1-mini'),",
|
|
347
|
+
" 'llm_route': 'google_api_key' if bool_env('OMAR_GOOGLE_KEY_PRESENT') else ('openai_api_key' if bool_env('OMAR_OPENAI_KEY_PRESENT') else 'sentinelayer_managed'),",
|
|
348
|
+
" 'google_key_present': bool_env('OMAR_GOOGLE_KEY_PRESENT'),",
|
|
349
|
+
" 'openai_key_present': bool_env('OMAR_OPENAI_KEY_PRESENT'),",
|
|
350
|
+
" 'managed_llm': bool_env('OMAR_MANAGED_LLM'),",
|
|
330
351
|
" 'llm_failure_policy': 'block',",
|
|
331
352
|
" },",
|
|
332
353
|
" 'github': {",
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
const SESSION_CONTROL_EVENT_TYPES = new Set([
|
|
2
|
+
"file_lock",
|
|
3
|
+
"file_unlock",
|
|
4
|
+
"file_lock_expired",
|
|
2
5
|
"listener_stop",
|
|
3
6
|
"session_coaching",
|
|
4
7
|
"session_listen_catchup",
|
|
5
8
|
"session_listen_error",
|
|
9
|
+
"session_reaction",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const QUIET_SESSION_ACTION_TYPES = new Set([
|
|
13
|
+
"ack",
|
|
14
|
+
"dislike",
|
|
15
|
+
"disregard",
|
|
16
|
+
"like",
|
|
17
|
+
"view",
|
|
6
18
|
]);
|
|
7
19
|
|
|
8
20
|
function normalizeString(value) {
|
|
@@ -28,8 +40,13 @@ export function isSessionListenerLifecycleEvent(event = {}) {
|
|
|
28
40
|
|
|
29
41
|
export function isSessionControlEvent(event = {}) {
|
|
30
42
|
const type = eventType(event);
|
|
43
|
+
const payload = event?.payload && typeof event.payload === "object" && !Array.isArray(event.payload)
|
|
44
|
+
? event.payload
|
|
45
|
+
: {};
|
|
46
|
+
const actionType = normalizeString(payload.actionType || payload.action_type || payload.reaction).toLowerCase();
|
|
31
47
|
return (
|
|
32
48
|
isSessionListenerLifecycleEvent(event) ||
|
|
49
|
+
(type === "session_action" && QUIET_SESSION_ACTION_TYPES.has(actionType)) ||
|
|
33
50
|
type.startsWith("session_listen_") ||
|
|
34
51
|
SESSION_CONTROL_EVENT_TYPES.has(type)
|
|
35
52
|
);
|