@cdot65/daystrom 1.1.1 → 1.2.0

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/airs/management.d.ts.map +1 -1
  3. package/dist/airs/management.js +2 -7
  4. package/dist/airs/management.js.map +1 -1
  5. package/dist/airs/promptsets.d.ts +24 -0
  6. package/dist/airs/promptsets.d.ts.map +1 -0
  7. package/dist/airs/promptsets.js +35 -0
  8. package/dist/airs/promptsets.js.map +1 -0
  9. package/dist/airs/redteam.d.ts +35 -0
  10. package/dist/airs/redteam.d.ts.map +1 -0
  11. package/dist/airs/redteam.js +167 -0
  12. package/dist/airs/redteam.js.map +1 -0
  13. package/dist/airs/types.d.ts +135 -1
  14. package/dist/airs/types.d.ts.map +1 -1
  15. package/dist/airs/types.js +1 -1
  16. package/dist/cli/commands/generate.d.ts.map +1 -1
  17. package/dist/cli/commands/generate.js +18 -0
  18. package/dist/cli/commands/generate.js.map +1 -1
  19. package/dist/cli/commands/redteam.d.ts +4 -0
  20. package/dist/cli/commands/redteam.d.ts.map +1 -0
  21. package/dist/cli/commands/redteam.js +203 -0
  22. package/dist/cli/commands/redteam.js.map +1 -0
  23. package/dist/cli/commands/resume.d.ts.map +1 -1
  24. package/dist/cli/commands/resume.js +18 -0
  25. package/dist/cli/commands/resume.js.map +1 -1
  26. package/dist/cli/index.js +2 -0
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/cli/renderer.d.ts +88 -0
  29. package/dist/cli/renderer.d.ts.map +1 -1
  30. package/dist/cli/renderer.js +174 -0
  31. package/dist/cli/renderer.js.map +1 -1
  32. package/dist/core/loop.d.ts +3 -1
  33. package/dist/core/loop.d.ts.map +1 -1
  34. package/dist/core/loop.js +17 -0
  35. package/dist/core/loop.js.map +1 -1
  36. package/dist/core/types.d.ts +7 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/llm/schemas.d.ts +10 -10
  43. package/package.json +1 -1
@@ -0,0 +1,203 @@
1
+ import { SdkRedTeamService } from '../../airs/redteam.js';
2
+ import { loadConfig } from '../../config/loader.js';
3
+ import { renderAttackList, renderCategories, renderCustomReport, renderError, renderRedteamHeader, renderScanList, renderScanProgress, renderScanStatus, renderStaticReport, renderTargetList, } from '../renderer.js';
4
+ /** Create an SdkRedTeamService from config. */
5
+ async function createService() {
6
+ const config = await loadConfig();
7
+ return new SdkRedTeamService({
8
+ clientId: config.mgmtClientId,
9
+ clientSecret: config.mgmtClientSecret,
10
+ tsgId: config.mgmtTsgId,
11
+ tokenEndpoint: config.mgmtTokenEndpoint,
12
+ });
13
+ }
14
+ /** Register the `redteam` command group. */
15
+ export function registerRedteamCommand(program) {
16
+ const redteam = program.command('redteam').description('AI Red Team scan operations');
17
+ // -----------------------------------------------------------------------
18
+ // redteam scan — execute a red team scan
19
+ // -----------------------------------------------------------------------
20
+ redteam
21
+ .command('scan')
22
+ .description('Execute a red team scan against a target')
23
+ .requiredOption('--target <uuid>', 'Target UUID')
24
+ .requiredOption('--name <name>', 'Scan name')
25
+ .option('--type <type>', 'Job type: STATIC, DYNAMIC, or CUSTOM', 'STATIC')
26
+ .option('--categories <json>', 'Category filter JSON (STATIC scans)')
27
+ .option('--prompt-sets <uuids>', 'Comma-separated prompt set UUIDs (CUSTOM scans)')
28
+ .option('--no-wait', 'Submit scan without waiting for completion')
29
+ .action(async (opts) => {
30
+ try {
31
+ renderRedteamHeader();
32
+ const service = await createService();
33
+ let categories;
34
+ if (opts.categories) {
35
+ categories = JSON.parse(opts.categories);
36
+ }
37
+ const customPromptSets = opts.promptSets
38
+ ? opts.promptSets.split(',').map((s) => s.trim())
39
+ : undefined;
40
+ console.log(` Creating ${opts.type} scan "${opts.name}"...`);
41
+ const job = await service.createScan({
42
+ name: opts.name,
43
+ targetUuid: opts.target,
44
+ jobType: opts.type,
45
+ categories,
46
+ customPromptSets,
47
+ });
48
+ renderScanStatus(job);
49
+ if (opts.wait !== false) {
50
+ console.log(' Waiting for completion...\n');
51
+ const completed = await service.waitForCompletion(job.uuid, (progress) => renderScanProgress(progress));
52
+ console.log('\n');
53
+ renderScanStatus(completed);
54
+ console.log(` Job ID: ${completed.uuid}`);
55
+ console.log(' Run `daystrom redteam report <jobId>` to view results.\n');
56
+ }
57
+ else {
58
+ console.log(` Job ID: ${job.uuid}`);
59
+ console.log(' Run `daystrom redteam status <jobId>` to check progress.\n');
60
+ }
61
+ }
62
+ catch (err) {
63
+ renderError(err instanceof Error ? err.message : String(err));
64
+ process.exit(1);
65
+ }
66
+ });
67
+ // -----------------------------------------------------------------------
68
+ // redteam status — check scan status
69
+ // -----------------------------------------------------------------------
70
+ redteam
71
+ .command('status <jobId>')
72
+ .description('Check scan status')
73
+ .action(async (jobId) => {
74
+ try {
75
+ renderRedteamHeader();
76
+ const service = await createService();
77
+ const job = await service.getScan(jobId);
78
+ renderScanStatus(job);
79
+ }
80
+ catch (err) {
81
+ renderError(err instanceof Error ? err.message : String(err));
82
+ process.exit(1);
83
+ }
84
+ });
85
+ // -----------------------------------------------------------------------
86
+ // redteam report — view scan report
87
+ // -----------------------------------------------------------------------
88
+ redteam
89
+ .command('report <jobId>')
90
+ .description('View scan report')
91
+ .option('--attacks', 'Include attack list', false)
92
+ .option('--severity <level>', 'Filter attacks by severity')
93
+ .option('--limit <n>', 'Max attacks to show', '20')
94
+ .action(async (jobId, opts) => {
95
+ try {
96
+ renderRedteamHeader();
97
+ const service = await createService();
98
+ const job = await service.getScan(jobId);
99
+ renderScanStatus(job);
100
+ if (job.jobType === 'CUSTOM') {
101
+ const report = await service.getCustomReport(jobId);
102
+ renderCustomReport(report);
103
+ }
104
+ else {
105
+ const report = await service.getStaticReport(jobId);
106
+ renderStaticReport(report);
107
+ }
108
+ if (opts.attacks) {
109
+ const attacks = await service.listAttacks(jobId, {
110
+ severity: opts.severity,
111
+ limit: Number.parseInt(opts.limit, 10),
112
+ });
113
+ renderAttackList(attacks);
114
+ }
115
+ }
116
+ catch (err) {
117
+ renderError(err instanceof Error ? err.message : String(err));
118
+ process.exit(1);
119
+ }
120
+ });
121
+ // -----------------------------------------------------------------------
122
+ // redteam list — list recent scans
123
+ // -----------------------------------------------------------------------
124
+ redteam
125
+ .command('list')
126
+ .description('List recent scans')
127
+ .option('--status <status>', 'Filter by status (QUEUED, RUNNING, COMPLETED, FAILED, ABORTED)')
128
+ .option('--type <type>', 'Filter by job type (STATIC, DYNAMIC, CUSTOM)')
129
+ .option('--target <uuid>', 'Filter by target UUID')
130
+ .option('--limit <n>', 'Max results', '10')
131
+ .action(async (opts) => {
132
+ try {
133
+ renderRedteamHeader();
134
+ const service = await createService();
135
+ const scans = await service.listScans({
136
+ status: opts.status,
137
+ jobType: opts.type,
138
+ targetId: opts.target,
139
+ limit: Number.parseInt(opts.limit, 10),
140
+ });
141
+ renderScanList(scans);
142
+ }
143
+ catch (err) {
144
+ renderError(err instanceof Error ? err.message : String(err));
145
+ process.exit(1);
146
+ }
147
+ });
148
+ // -----------------------------------------------------------------------
149
+ // redteam targets — list configured targets
150
+ // -----------------------------------------------------------------------
151
+ redteam
152
+ .command('targets')
153
+ .description('List configured red team targets')
154
+ .action(async () => {
155
+ try {
156
+ renderRedteamHeader();
157
+ const service = await createService();
158
+ const targets = await service.listTargets();
159
+ renderTargetList(targets);
160
+ }
161
+ catch (err) {
162
+ renderError(err instanceof Error ? err.message : String(err));
163
+ process.exit(1);
164
+ }
165
+ });
166
+ // -----------------------------------------------------------------------
167
+ // redteam categories — list attack categories
168
+ // -----------------------------------------------------------------------
169
+ redteam
170
+ .command('categories')
171
+ .description('List available attack categories')
172
+ .action(async () => {
173
+ try {
174
+ renderRedteamHeader();
175
+ const service = await createService();
176
+ const categories = await service.getCategories();
177
+ renderCategories(categories);
178
+ }
179
+ catch (err) {
180
+ renderError(err instanceof Error ? err.message : String(err));
181
+ process.exit(1);
182
+ }
183
+ });
184
+ // -----------------------------------------------------------------------
185
+ // redteam abort — abort a running scan
186
+ // -----------------------------------------------------------------------
187
+ redteam
188
+ .command('abort <jobId>')
189
+ .description('Abort a running scan')
190
+ .action(async (jobId) => {
191
+ try {
192
+ renderRedteamHeader();
193
+ const service = await createService();
194
+ await service.abortScan(jobId);
195
+ console.log(` Scan ${jobId} aborted.\n`);
196
+ }
197
+ catch (err) {
198
+ renderError(err instanceof Error ? err.message : String(err));
199
+ process.exit(1);
200
+ }
201
+ });
202
+ }
203
+ //# sourceMappingURL=redteam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redteam.js","sourceRoot":"","sources":["../../../src/cli/commands/redteam.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBzC,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmG5D"}
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBzC,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuH5D"}
@@ -1,5 +1,6 @@
1
1
  import * as path from 'node:path';
2
2
  import { SdkManagementService } from '../../airs/management.js';
3
+ import { SdkPromptSetService } from '../../airs/promptsets.js';
3
4
  import { AirsScanService, DebugScanService } from '../../airs/scanner.js';
4
5
  import { loadConfig } from '../../config/loader.js';
5
6
  import { runLoop } from '../../core/loop.js';
@@ -14,6 +15,8 @@ export function registerResumeCommand(program) {
14
15
  .description('Resume a paused or failed run')
15
16
  .option('--max-iterations <n>', 'Additional iterations to run', '10')
16
17
  .option('--debug-scans', 'Dump raw AIRS scan responses to JSONL for debugging', false)
18
+ .option('--create-prompt-set', 'Create custom prompt set from test cases after loop', false)
19
+ .option('--prompt-set-name <name>', 'Override auto-generated prompt set name')
17
20
  .action(async (runId, opts) => {
18
21
  try {
19
22
  renderHeader();
@@ -33,6 +36,8 @@ export function registerResumeCommand(program) {
33
36
  const userInput = {
34
37
  ...existingRun.userInput,
35
38
  maxIterations: existingRun.currentIteration + additionalIterations,
39
+ createPromptSet: opts.createPromptSet ?? false,
40
+ promptSetName: opts.promptSetName,
36
41
  };
37
42
  const model = await createLlmProvider({
38
43
  provider: config.llmProvider,
@@ -60,12 +65,22 @@ export function registerResumeCommand(program) {
60
65
  apiEndpoint: config.mgmtEndpoint,
61
66
  tokenEndpoint: config.mgmtTokenEndpoint,
62
67
  });
68
+ // Set up prompt set service if requested
69
+ const promptSets = userInput.createPromptSet
70
+ ? new SdkPromptSetService({
71
+ clientId: config.mgmtClientId,
72
+ clientSecret: config.mgmtClientSecret,
73
+ tsgId: config.mgmtTsgId,
74
+ tokenEndpoint: config.mgmtTokenEndpoint,
75
+ })
76
+ : undefined;
63
77
  console.log(` Resuming run ${runId} from iteration ${existingRun.currentIteration}...`);
64
78
  for await (const event of runLoop(userInput, {
65
79
  llm,
66
80
  management,
67
81
  scanner,
68
82
  propagationDelayMs: config.propagationDelayMs,
83
+ promptSets,
69
84
  })) {
70
85
  switch (event.type) {
71
86
  case 'iteration:start':
@@ -89,6 +104,9 @@ export function registerResumeCommand(program) {
89
104
  case 'iteration:complete':
90
105
  renderIterationSummary(event.result);
91
106
  break;
107
+ case 'promptset:created':
108
+ console.log(` ✓ Custom prompt set created: ${event.promptSetName} (${event.promptCount} prompts)`);
109
+ break;
92
110
  case 'loop:complete':
93
111
  await store.save(event.runState);
94
112
  renderLoopComplete(event.runState);
@@ -1 +1 @@
1
- {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -3,6 +3,7 @@ import 'dotenv/config';
3
3
  import { Command } from 'commander';
4
4
  import { registerGenerateCommand } from './commands/generate.js';
5
5
  import { registerListCommand } from './commands/list.js';
6
+ import { registerRedteamCommand } from './commands/redteam.js';
6
7
  import { registerReportCommand } from './commands/report.js';
7
8
  import { registerResumeCommand } from './commands/resume.js';
8
9
  const program = new Command();
@@ -14,5 +15,6 @@ registerGenerateCommand(program);
14
15
  registerResumeCommand(program);
15
16
  registerReportCommand(program);
16
17
  registerListCommand(program);
18
+ registerRedteamCommand(program);
17
19
  program.parse();
18
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAEhC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -24,6 +24,94 @@ export declare function renderIterationSummary(result: IterationResult): void;
24
24
  export declare function renderMemoryLoaded(learningCount: number): void;
25
25
  /** Render count of learnings extracted from the current run. */
26
26
  export declare function renderMemoryExtracted(learningCount: number): void;
27
+ /** Render the red team banner. */
28
+ export declare function renderRedteamHeader(): void;
29
+ /** Render a scan's status summary. */
30
+ export declare function renderScanStatus(job: {
31
+ uuid: string;
32
+ name: string;
33
+ status: string;
34
+ jobType: string;
35
+ targetName?: string;
36
+ score?: number | null;
37
+ asr?: number | null;
38
+ completed?: number | null;
39
+ total?: number | null;
40
+ }): void;
41
+ /** Render a table of scans. */
42
+ export declare function renderScanList(jobs: Array<{
43
+ uuid: string;
44
+ name: string;
45
+ status: string;
46
+ jobType: string;
47
+ score?: number | null;
48
+ createdAt?: string | null;
49
+ }>): void;
50
+ /** Render a static scan report. */
51
+ export declare function renderStaticReport(report: {
52
+ score?: number | null;
53
+ asr?: number | null;
54
+ severityBreakdown: Array<{
55
+ severity: string;
56
+ successful: number;
57
+ failed: number;
58
+ }>;
59
+ reportSummary?: string | null;
60
+ categories: Array<{
61
+ id: string;
62
+ displayName: string;
63
+ asr: number;
64
+ successful: number;
65
+ failed: number;
66
+ total: number;
67
+ }>;
68
+ }): void;
69
+ /** Render a custom attack report. */
70
+ export declare function renderCustomReport(report: {
71
+ totalPrompts: number;
72
+ totalAttacks: number;
73
+ totalThreats: number;
74
+ score: number;
75
+ asr: number;
76
+ promptSets: Array<{
77
+ promptSetName: string;
78
+ totalPrompts: number;
79
+ totalThreats: number;
80
+ threatRate: number;
81
+ }>;
82
+ }): void;
83
+ /** Render attack list with severity coloring. */
84
+ export declare function renderAttackList(attacks: Array<{
85
+ name: string;
86
+ severity?: string;
87
+ category?: string;
88
+ successful: boolean;
89
+ }>): void;
90
+ /** Render target list. */
91
+ export declare function renderTargetList(targets: Array<{
92
+ uuid: string;
93
+ name: string;
94
+ status: string;
95
+ targetType?: string;
96
+ active: boolean;
97
+ }>): void;
98
+ /** Render attack category tree. */
99
+ export declare function renderCategories(categories: Array<{
100
+ id: string;
101
+ displayName: string;
102
+ description?: string;
103
+ subCategories: Array<{
104
+ id: string;
105
+ displayName: string;
106
+ description?: string;
107
+ }>;
108
+ }>): void;
109
+ /** Render polling progress inline. */
110
+ export declare function renderScanProgress(job: {
111
+ status: string;
112
+ completed?: number | null;
113
+ total?: number | null;
114
+ }): void;
27
115
  /** Render accumulated test count with optional dropped info. */
28
116
  export declare function renderTestsAccumulated(newCount: number, totalCount: number, droppedCount: number): void;
29
117
  //# sourceMappingURL=renderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/cli/renderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,qCAAqC;AACrC,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED,sCAAsC;AACtC,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED,wDAAwD;AACxD,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAQpD;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAKzE;AAED,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAe5D;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAe7D;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAW3D;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,IAAI,CAsB3D;AAED,yCAAyC;AACzC,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAUpE;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAM9D;AAED,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAEjE;AAED,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/cli/renderer.ts"],"names":[],"mappings":"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"}
@@ -112,6 +112,180 @@ export function renderMemoryLoaded(learningCount) {
112
112
  export function renderMemoryExtracted(learningCount) {
113
113
  console.log(chalk.cyan(` Memory: extracted ${learningCount} learnings from this run`));
114
114
  }
115
+ // ---------------------------------------------------------------------------
116
+ // Red Team rendering
117
+ // ---------------------------------------------------------------------------
118
+ /** Render the red team banner. */
119
+ export function renderRedteamHeader() {
120
+ console.log(chalk.bold.red('\n Prisma AIRS — AI Red Team'));
121
+ console.log(chalk.dim(' Adversarial scan operations\n'));
122
+ }
123
+ /** Severity → chalk color mapping. */
124
+ function severityColor(severity) {
125
+ switch (severity.toUpperCase()) {
126
+ case 'CRITICAL':
127
+ return chalk.red;
128
+ case 'HIGH':
129
+ return chalk.magenta;
130
+ case 'MEDIUM':
131
+ return chalk.yellow;
132
+ case 'LOW':
133
+ return chalk.cyan;
134
+ default:
135
+ return chalk.dim;
136
+ }
137
+ }
138
+ /** Status → chalk color mapping. */
139
+ function statusColor(status) {
140
+ switch (status) {
141
+ case 'COMPLETED':
142
+ return chalk.green;
143
+ case 'RUNNING':
144
+ return chalk.blue;
145
+ case 'QUEUED':
146
+ case 'INIT':
147
+ return chalk.yellow;
148
+ case 'FAILED':
149
+ case 'ABORTED':
150
+ return chalk.red;
151
+ case 'PARTIALLY_COMPLETE':
152
+ return chalk.yellow;
153
+ default:
154
+ return chalk.white;
155
+ }
156
+ }
157
+ /** Render a scan's status summary. */
158
+ export function renderScanStatus(job) {
159
+ console.log(chalk.bold(' Scan Status:'));
160
+ console.log(` ID: ${chalk.dim(job.uuid)}`);
161
+ console.log(` Name: ${job.name}`);
162
+ console.log(` Type: ${job.jobType}`);
163
+ if (job.targetName)
164
+ console.log(` Target: ${job.targetName}`);
165
+ console.log(` Status: ${statusColor(job.status)(job.status)}`);
166
+ if (job.total != null && job.completed != null) {
167
+ console.log(` Progress: ${job.completed}/${job.total}`);
168
+ }
169
+ if (job.score != null)
170
+ console.log(` Score: ${job.score}`);
171
+ if (job.asr != null)
172
+ console.log(` ASR: ${(job.asr * 100).toFixed(1)}%`);
173
+ console.log();
174
+ }
175
+ /** Render a table of scans. */
176
+ export function renderScanList(jobs) {
177
+ if (jobs.length === 0) {
178
+ console.log(chalk.dim(' No scans found.\n'));
179
+ return;
180
+ }
181
+ console.log(chalk.bold('\n Recent Scans:\n'));
182
+ for (const job of jobs) {
183
+ console.log(` ${chalk.dim(job.uuid)}`);
184
+ console.log(` ${job.name} ${statusColor(job.status)(job.status)} ${job.jobType}${job.score != null ? ` score: ${job.score}` : ''}`);
185
+ if (job.createdAt)
186
+ console.log(` ${chalk.dim(job.createdAt)}`);
187
+ console.log();
188
+ }
189
+ }
190
+ /** Render a static scan report. */
191
+ export function renderStaticReport(report) {
192
+ console.log(chalk.bold('\n Static Scan Report:'));
193
+ if (report.score != null)
194
+ console.log(` Score: ${report.score}`);
195
+ if (report.asr != null)
196
+ console.log(` ASR: ${(report.asr * 100).toFixed(1)}%`);
197
+ if (report.severityBreakdown.length > 0) {
198
+ console.log(chalk.bold('\n Severity Breakdown:'));
199
+ for (const s of report.severityBreakdown) {
200
+ const color = severityColor(s.severity);
201
+ console.log(` ${color(s.severity.padEnd(10))} ${chalk.red(`${s.successful} bypassed`)} ${chalk.green(`${s.failed} blocked`)}`);
202
+ }
203
+ }
204
+ if (report.categories.length > 0) {
205
+ console.log(chalk.bold('\n Categories:'));
206
+ for (const c of report.categories) {
207
+ const asrPct = (c.asr * 100).toFixed(1);
208
+ console.log(` ${c.displayName.padEnd(30)} ASR: ${asrPct}% (${c.successful}/${c.total})`);
209
+ }
210
+ }
211
+ if (report.reportSummary) {
212
+ console.log(chalk.bold('\n Summary:'));
213
+ console.log(` ${report.reportSummary}`);
214
+ }
215
+ console.log();
216
+ }
217
+ /** Render a custom attack report. */
218
+ export function renderCustomReport(report) {
219
+ console.log(chalk.bold('\n Custom Attack Report:'));
220
+ console.log(` Score: ${report.score}`);
221
+ console.log(` ASR: ${(report.asr * 100).toFixed(1)}%`);
222
+ console.log(` Attacks: ${report.totalAttacks} Threats: ${report.totalThreats}`);
223
+ if (report.promptSets.length > 0) {
224
+ console.log(chalk.bold('\n Prompt Sets:'));
225
+ for (const ps of report.promptSets) {
226
+ console.log(` ${ps.promptSetName.padEnd(40)} ${ps.totalThreats}/${ps.totalPrompts} threats (${(ps.threatRate * 100).toFixed(1)}%)`);
227
+ }
228
+ }
229
+ console.log();
230
+ }
231
+ /** Render attack list with severity coloring. */
232
+ export function renderAttackList(attacks) {
233
+ if (attacks.length === 0) {
234
+ console.log(chalk.dim(' No attacks found.\n'));
235
+ return;
236
+ }
237
+ console.log(chalk.bold('\n Attacks:\n'));
238
+ for (const a of attacks) {
239
+ const sev = a.severity
240
+ ? severityColor(a.severity)(a.severity.padEnd(10))
241
+ : chalk.dim('N/A'.padEnd(10));
242
+ const result = a.successful ? chalk.red('BYPASSED') : chalk.green('BLOCKED');
243
+ console.log(` ${sev} ${result} ${a.name}${a.category ? chalk.dim(` [${a.category}]`) : ''}`);
244
+ }
245
+ console.log();
246
+ }
247
+ /** Render target list. */
248
+ export function renderTargetList(targets) {
249
+ if (targets.length === 0) {
250
+ console.log(chalk.dim(' No targets found.\n'));
251
+ return;
252
+ }
253
+ console.log(chalk.bold('\n Targets:\n'));
254
+ for (const t of targets) {
255
+ console.log(` ${chalk.dim(t.uuid)}`);
256
+ console.log(` ${t.name} ${statusColor(t.active ? 'COMPLETED' : 'FAILED')(t.active ? 'active' : 'inactive')}${t.targetType ? ` type: ${t.targetType}` : ''}`);
257
+ }
258
+ console.log();
259
+ }
260
+ /** Render attack category tree. */
261
+ export function renderCategories(categories) {
262
+ if (categories.length === 0) {
263
+ console.log(chalk.dim(' No categories found.\n'));
264
+ return;
265
+ }
266
+ console.log(chalk.bold('\n Attack Categories:\n'));
267
+ for (const c of categories) {
268
+ console.log(` ${chalk.bold(c.displayName)}${c.description ? chalk.dim(` — ${c.description}`) : ''}`);
269
+ for (const sc of c.subCategories) {
270
+ console.log(` ${chalk.dim('•')} ${sc.displayName}${sc.description ? chalk.dim(` — ${sc.description}`) : ''}`);
271
+ }
272
+ console.log();
273
+ }
274
+ }
275
+ /** Render polling progress inline. */
276
+ export function renderScanProgress(job) {
277
+ if (job.total != null && job.completed != null && job.total > 0) {
278
+ const pct = Math.round((job.completed / job.total) * 100);
279
+ const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));
280
+ process.stdout.write(`\r ${statusColor(job.status)(job.status)} ${bar} ${pct}% (${job.completed}/${job.total})`);
281
+ }
282
+ else {
283
+ process.stdout.write(`\r ${statusColor(job.status)(job.status)}...`);
284
+ }
285
+ }
286
+ // ---------------------------------------------------------------------------
287
+ // Guardrail loop rendering
288
+ // ---------------------------------------------------------------------------
115
289
  /** Render accumulated test count with optional dropped info. */
116
290
  export function renderTestsAccumulated(newCount, totalCount, droppedCount) {
117
291
  let msg = ` Tests: ${newCount} new, ${totalCount} total (accumulated)`;