@cdot65/prisma-airs-cli 2.12.0 → 3.0.1
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 +27 -169
- package/dist/chunk-JXHYQFEK.js +1752 -0
- package/dist/cli/index.js +6511 -4
- package/dist/dlp-5XKWITZX.js +710 -0
- package/dist/index.d.ts +1396 -26
- package/dist/index.js +40 -37
- package/package.json +22 -25
- package/dist/airs/dlp/data-filtering-profiles.d.ts +0 -10
- package/dist/airs/dlp/data-filtering-profiles.d.ts.map +0 -1
- package/dist/airs/dlp/data-filtering-profiles.js +0 -17
- package/dist/airs/dlp/data-filtering-profiles.js.map +0 -1
- package/dist/airs/dlp/data-patterns.d.ts +0 -13
- package/dist/airs/dlp/data-patterns.d.ts.map +0 -1
- package/dist/airs/dlp/data-patterns.js +0 -26
- package/dist/airs/dlp/data-patterns.js.map +0 -1
- package/dist/airs/dlp/data-profiles.d.ts +0 -12
- package/dist/airs/dlp/data-profiles.d.ts.map +0 -1
- package/dist/airs/dlp/data-profiles.js +0 -23
- package/dist/airs/dlp/data-profiles.js.map +0 -1
- package/dist/airs/dlp/dictionaries.d.ts +0 -13
- package/dist/airs/dlp/dictionaries.d.ts.map +0 -1
- package/dist/airs/dlp/dictionaries.js +0 -34
- package/dist/airs/dlp/dictionaries.js.map +0 -1
- package/dist/airs/dlp/index.d.ts +0 -6
- package/dist/airs/dlp/index.d.ts.map +0 -1
- package/dist/airs/dlp/index.js +0 -6
- package/dist/airs/dlp/index.js.map +0 -1
- package/dist/airs/dlp/types.d.ts +0 -36
- package/dist/airs/dlp/types.d.ts.map +0 -1
- package/dist/airs/dlp/types.js +0 -2
- package/dist/airs/dlp/types.js.map +0 -1
- package/dist/airs/management.d.ts +0 -69
- package/dist/airs/management.d.ts.map +0 -1
- package/dist/airs/management.js +0 -370
- package/dist/airs/management.js.map +0 -1
- package/dist/airs/modelsecurity.d.ts +0 -74
- package/dist/airs/modelsecurity.d.ts.map +0 -1
- package/dist/airs/modelsecurity.js +0 -337
- package/dist/airs/modelsecurity.js.map +0 -1
- package/dist/airs/promptsets.d.ts +0 -50
- package/dist/airs/promptsets.d.ts.map +0 -1
- package/dist/airs/promptsets.js +0 -120
- package/dist/airs/promptsets.js.map +0 -1
- package/dist/airs/redteam.d.ts +0 -75
- package/dist/airs/redteam.d.ts.map +0 -1
- package/dist/airs/redteam.js +0 -418
- package/dist/airs/redteam.js.map +0 -1
- package/dist/airs/runtime.d.ts +0 -27
- package/dist/airs/runtime.d.ts.map +0 -1
- package/dist/airs/runtime.js +0 -158
- package/dist/airs/runtime.js.map +0 -1
- package/dist/airs/scanner.d.ts +0 -36
- package/dist/airs/scanner.d.ts.map +0 -1
- package/dist/airs/scanner.js +0 -101
- package/dist/airs/scanner.js.map +0 -1
- package/dist/airs/types.d.ts +0 -838
- package/dist/airs/types.d.ts.map +0 -1
- package/dist/airs/types.js +0 -6
- package/dist/airs/types.js.map +0 -1
- package/dist/audit/evaluator.d.ts +0 -17
- package/dist/audit/evaluator.d.ts.map +0 -1
- package/dist/audit/evaluator.js +0 -82
- package/dist/audit/evaluator.js.map +0 -1
- package/dist/audit/report.d.ts +0 -38
- package/dist/audit/report.d.ts.map +0 -1
- package/dist/audit/report.js +0 -141
- package/dist/audit/report.js.map +0 -1
- package/dist/audit/runner.d.ts +0 -18
- package/dist/audit/runner.d.ts.map +0 -1
- package/dist/audit/runner.js +0 -60
- package/dist/audit/runner.js.map +0 -1
- package/dist/audit/types.d.ts +0 -53
- package/dist/audit/types.d.ts.map +0 -1
- package/dist/audit/types.js +0 -5
- package/dist/audit/types.js.map +0 -1
- package/dist/backup/index.d.ts +0 -3
- package/dist/backup/index.d.ts.map +0 -1
- package/dist/backup/index.js +0 -3
- package/dist/backup/index.js.map +0 -1
- package/dist/backup/io.d.ts +0 -15
- package/dist/backup/io.d.ts.map +0 -1
- package/dist/backup/io.js +0 -67
- package/dist/backup/io.js.map +0 -1
- package/dist/backup/types.d.ts +0 -25
- package/dist/backup/types.d.ts.map +0 -1
- package/dist/backup/types.js +0 -2
- package/dist/backup/types.js.map +0 -1
- package/dist/cli/builders/profile-builder.d.ts +0 -45
- package/dist/cli/builders/profile-builder.d.ts.map +0 -1
- package/dist/cli/builders/profile-builder.js +0 -308
- package/dist/cli/builders/profile-builder.js.map +0 -1
- package/dist/cli/bulk-scan-state.d.ts +0 -15
- package/dist/cli/bulk-scan-state.d.ts.map +0 -1
- package/dist/cli/bulk-scan-state.js +0 -20
- package/dist/cli/bulk-scan-state.js.map +0 -1
- package/dist/cli/commands/audit.d.ts +0 -4
- package/dist/cli/commands/audit.d.ts.map +0 -1
- package/dist/cli/commands/audit.js +0 -113
- package/dist/cli/commands/audit.js.map +0 -1
- package/dist/cli/commands/backup.d.ts +0 -10
- package/dist/cli/commands/backup.d.ts.map +0 -1
- package/dist/cli/commands/backup.js +0 -102
- package/dist/cli/commands/backup.js.map +0 -1
- package/dist/cli/commands/dlp/build-body.d.ts +0 -28
- package/dist/cli/commands/dlp/build-body.d.ts.map +0 -1
- package/dist/cli/commands/dlp/build-body.js +0 -174
- package/dist/cli/commands/dlp/build-body.js.map +0 -1
- package/dist/cli/commands/dlp/dictionaries.d.ts +0 -3
- package/dist/cli/commands/dlp/dictionaries.d.ts.map +0 -1
- package/dist/cli/commands/dlp/dictionaries.js +0 -167
- package/dist/cli/commands/dlp/dictionaries.js.map +0 -1
- package/dist/cli/commands/dlp/filtering-profiles.d.ts +0 -3
- package/dist/cli/commands/dlp/filtering-profiles.d.ts.map +0 -1
- package/dist/cli/commands/dlp/filtering-profiles.js +0 -82
- package/dist/cli/commands/dlp/filtering-profiles.js.map +0 -1
- package/dist/cli/commands/dlp/generate.d.ts +0 -3
- package/dist/cli/commands/dlp/generate.d.ts.map +0 -1
- package/dist/cli/commands/dlp/generate.js +0 -51
- package/dist/cli/commands/dlp/generate.js.map +0 -1
- package/dist/cli/commands/dlp/index.d.ts +0 -3
- package/dist/cli/commands/dlp/index.d.ts.map +0 -1
- package/dist/cli/commands/dlp/index.js +0 -16
- package/dist/cli/commands/dlp/index.js.map +0 -1
- package/dist/cli/commands/dlp/patch.d.ts +0 -15
- package/dist/cli/commands/dlp/patch.d.ts.map +0 -1
- package/dist/cli/commands/dlp/patch.js +0 -70
- package/dist/cli/commands/dlp/patch.js.map +0 -1
- package/dist/cli/commands/dlp/patterns.d.ts +0 -3
- package/dist/cli/commands/dlp/patterns.d.ts.map +0 -1
- package/dist/cli/commands/dlp/patterns.js +0 -127
- package/dist/cli/commands/dlp/patterns.js.map +0 -1
- package/dist/cli/commands/dlp/profiles.d.ts +0 -3
- package/dist/cli/commands/dlp/profiles.d.ts.map +0 -1
- package/dist/cli/commands/dlp/profiles.js +0 -126
- package/dist/cli/commands/dlp/profiles.js.map +0 -1
- package/dist/cli/commands/modelsecurity.d.ts +0 -4
- package/dist/cli/commands/modelsecurity.d.ts.map +0 -1
- package/dist/cli/commands/modelsecurity.js +0 -605
- package/dist/cli/commands/modelsecurity.js.map +0 -1
- package/dist/cli/commands/profiles-cleanup.d.ts +0 -26
- package/dist/cli/commands/profiles-cleanup.d.ts.map +0 -1
- package/dist/cli/commands/profiles-cleanup.js +0 -113
- package/dist/cli/commands/profiles-cleanup.js.map +0 -1
- package/dist/cli/commands/redteam.d.ts +0 -12
- package/dist/cli/commands/redteam.d.ts.map +0 -1
- package/dist/cli/commands/redteam.js +0 -1075
- package/dist/cli/commands/redteam.js.map +0 -1
- package/dist/cli/commands/restore.d.ts +0 -11
- package/dist/cli/commands/restore.d.ts.map +0 -1
- package/dist/cli/commands/restore.js +0 -109
- package/dist/cli/commands/restore.js.map +0 -1
- package/dist/cli/commands/runtime.d.ts +0 -6
- package/dist/cli/commands/runtime.d.ts.map +0 -1
- package/dist/cli/commands/runtime.js +0 -869
- package/dist/cli/commands/runtime.js.map +0 -1
- package/dist/cli/commands/topics-apply.d.ts +0 -16
- package/dist/cli/commands/topics-apply.d.ts.map +0 -1
- package/dist/cli/commands/topics-apply.js +0 -69
- package/dist/cli/commands/topics-apply.js.map +0 -1
- package/dist/cli/commands/topics-create.d.ts +0 -16
- package/dist/cli/commands/topics-create.d.ts.map +0 -1
- package/dist/cli/commands/topics-create.js +0 -71
- package/dist/cli/commands/topics-create.js.map +0 -1
- package/dist/cli/commands/topics-eval.d.ts +0 -7
- package/dist/cli/commands/topics-eval.d.ts.map +0 -1
- package/dist/cli/commands/topics-eval.js +0 -65
- package/dist/cli/commands/topics-eval.js.map +0 -1
- package/dist/cli/commands/topics-revert.d.ts +0 -9
- package/dist/cli/commands/topics-revert.d.ts.map +0 -1
- package/dist/cli/commands/topics-revert.js +0 -52
- package/dist/cli/commands/topics-revert.js.map +0 -1
- package/dist/cli/commands/topics-sample.d.ts +0 -4
- package/dist/cli/commands/topics-sample.d.ts.map +0 -1
- package/dist/cli/commands/topics-sample.js +0 -27
- package/dist/cli/commands/topics-sample.js.map +0 -1
- package/dist/cli/debug-logger.d.ts +0 -11
- package/dist/cli/debug-logger.d.ts.map +0 -1
- package/dist/cli/debug-logger.js +0 -137
- package/dist/cli/debug-logger.js.map +0 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/parse-input.d.ts +0 -9
- package/dist/cli/parse-input.d.ts.map +0 -1
- package/dist/cli/parse-input.js +0 -109
- package/dist/cli/parse-input.js.map +0 -1
- package/dist/cli/program.d.ts +0 -3
- package/dist/cli/program.d.ts.map +0 -1
- package/dist/cli/program.js +0 -32
- package/dist/cli/program.js.map +0 -1
- package/dist/cli/renderer/audit.d.ts +0 -11
- package/dist/cli/renderer/audit.d.ts.map +0 -1
- package/dist/cli/renderer/audit.js +0 -58
- package/dist/cli/renderer/audit.js.map +0 -1
- package/dist/cli/renderer/backup.d.ts +0 -8
- package/dist/cli/renderer/backup.d.ts.map +0 -1
- package/dist/cli/renderer/backup.js +0 -50
- package/dist/cli/renderer/backup.js.map +0 -1
- package/dist/cli/renderer/common.d.ts +0 -14
- package/dist/cli/renderer/common.d.ts.map +0 -1
- package/dist/cli/renderer/common.js +0 -50
- package/dist/cli/renderer/common.js.map +0 -1
- package/dist/cli/renderer/dlp.d.ts +0 -34
- package/dist/cli/renderer/dlp.d.ts.map +0 -1
- package/dist/cli/renderer/dlp.js +0 -348
- package/dist/cli/renderer/dlp.js.map +0 -1
- package/dist/cli/renderer/eval.d.ts +0 -30
- package/dist/cli/renderer/eval.d.ts.map +0 -1
- package/dist/cli/renderer/eval.js +0 -61
- package/dist/cli/renderer/eval.js.map +0 -1
- package/dist/cli/renderer/index.d.ts +0 -9
- package/dist/cli/renderer/index.d.ts.map +0 -1
- package/dist/cli/renderer/index.js +0 -9
- package/dist/cli/renderer/index.js.map +0 -1
- package/dist/cli/renderer/modelsecurity.d.ts +0 -35
- package/dist/cli/renderer/modelsecurity.d.ts.map +0 -1
- package/dist/cli/renderer/modelsecurity.js +0 -339
- package/dist/cli/renderer/modelsecurity.js.map +0 -1
- package/dist/cli/renderer/redteam.d.ts +0 -224
- package/dist/cli/renderer/redteam.d.ts.map +0 -1
- package/dist/cli/renderer/redteam.js +0 -500
- package/dist/cli/renderer/redteam.js.map +0 -1
- package/dist/cli/renderer/runtime.d.ts +0 -135
- package/dist/cli/renderer/runtime.d.ts.map +0 -1
- package/dist/cli/renderer/runtime.js +0 -426
- package/dist/cli/renderer/runtime.js.map +0 -1
- package/dist/config/loader.d.ts +0 -3
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js +0 -59
- package/dist/config/loader.js.map +0 -1
- package/dist/config/schema.d.ts +0 -63
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -38
- package/dist/config/schema.js.map +0 -1
- package/dist/core/constraints.d.ts +0 -19
- package/dist/core/constraints.d.ts.map +0 -1
- package/dist/core/constraints.js +0 -82
- package/dist/core/constraints.js.map +0 -1
- package/dist/core/metrics.d.ts +0 -13
- package/dist/core/metrics.d.ts.map +0 -1
- package/dist/core/metrics.js +0 -73
- package/dist/core/metrics.js.map +0 -1
- package/dist/core/prompt-loader.d.ts +0 -14
- package/dist/core/prompt-loader.d.ts.map +0 -1
- package/dist/core/prompt-loader.js +0 -122
- package/dist/core/prompt-loader.js.map +0 -1
- package/dist/core/types.d.ts +0 -96
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -5
- package/dist/core/types.js.map +0 -1
- package/dist/dlp/embed/docx.d.ts +0 -3
- package/dist/dlp/embed/docx.d.ts.map +0 -1
- package/dist/dlp/embed/docx.js +0 -63
- package/dist/dlp/embed/docx.js.map +0 -1
- package/dist/dlp/embed/index.d.ts +0 -6
- package/dist/dlp/embed/index.d.ts.map +0 -1
- package/dist/dlp/embed/index.js +0 -20
- package/dist/dlp/embed/index.js.map +0 -1
- package/dist/dlp/embed/jpeg.d.ts +0 -5
- package/dist/dlp/embed/jpeg.d.ts.map +0 -1
- package/dist/dlp/embed/jpeg.js +0 -67
- package/dist/dlp/embed/jpeg.js.map +0 -1
- package/dist/dlp/embed/overlay.d.ts +0 -7
- package/dist/dlp/embed/overlay.d.ts.map +0 -1
- package/dist/dlp/embed/overlay.js +0 -21
- package/dist/dlp/embed/overlay.js.map +0 -1
- package/dist/dlp/embed/pdf.d.ts +0 -7
- package/dist/dlp/embed/pdf.d.ts.map +0 -1
- package/dist/dlp/embed/pdf.js +0 -124
- package/dist/dlp/embed/pdf.js.map +0 -1
- package/dist/dlp/embed/png-chunks.d.ts +0 -12
- package/dist/dlp/embed/png-chunks.d.ts.map +0 -1
- package/dist/dlp/embed/png-chunks.js +0 -85
- package/dist/dlp/embed/png-chunks.js.map +0 -1
- package/dist/dlp/embed/png.d.ts +0 -5
- package/dist/dlp/embed/png.d.ts.map +0 -1
- package/dist/dlp/embed/png.js +0 -75
- package/dist/dlp/embed/png.js.map +0 -1
- package/dist/dlp/embed/svg.d.ts +0 -3
- package/dist/dlp/embed/svg.d.ts.map +0 -1
- package/dist/dlp/embed/svg.js +0 -43
- package/dist/dlp/embed/svg.js.map +0 -1
- package/dist/dlp/embed/zip.d.ts +0 -3
- package/dist/dlp/embed/zip.d.ts.map +0 -1
- package/dist/dlp/embed/zip.js +0 -25
- package/dist/dlp/embed/zip.js.map +0 -1
- package/dist/dlp/generate/docx.d.ts +0 -3
- package/dist/dlp/generate/docx.d.ts.map +0 -1
- package/dist/dlp/generate/docx.js +0 -24
- package/dist/dlp/generate/docx.js.map +0 -1
- package/dist/dlp/generate/index.d.ts +0 -5
- package/dist/dlp/generate/index.d.ts.map +0 -1
- package/dist/dlp/generate/index.js +0 -21
- package/dist/dlp/generate/index.js.map +0 -1
- package/dist/dlp/generate/jpeg.d.ts +0 -3
- package/dist/dlp/generate/jpeg.d.ts.map +0 -1
- package/dist/dlp/generate/jpeg.js +0 -8
- package/dist/dlp/generate/jpeg.js.map +0 -1
- package/dist/dlp/generate/pdf.d.ts +0 -3
- package/dist/dlp/generate/pdf.d.ts.map +0 -1
- package/dist/dlp/generate/pdf.js +0 -19
- package/dist/dlp/generate/pdf.js.map +0 -1
- package/dist/dlp/generate/png.d.ts +0 -3
- package/dist/dlp/generate/png.d.ts.map +0 -1
- package/dist/dlp/generate/png.js +0 -8
- package/dist/dlp/generate/png.js.map +0 -1
- package/dist/dlp/generate/raster.d.ts +0 -9
- package/dist/dlp/generate/raster.d.ts.map +0 -1
- package/dist/dlp/generate/raster.js +0 -20
- package/dist/dlp/generate/raster.js.map +0 -1
- package/dist/dlp/generate/svg.d.ts +0 -3
- package/dist/dlp/generate/svg.d.ts.map +0 -1
- package/dist/dlp/generate/svg.js +0 -17
- package/dist/dlp/generate/svg.js.map +0 -1
- package/dist/dlp/index.d.ts +0 -15
- package/dist/dlp/index.d.ts.map +0 -1
- package/dist/dlp/index.js +0 -54
- package/dist/dlp/index.js.map +0 -1
- package/dist/dlp/lorem.d.ts +0 -3
- package/dist/dlp/lorem.d.ts.map +0 -1
- package/dist/dlp/lorem.js +0 -65
- package/dist/dlp/lorem.js.map +0 -1
- package/dist/dlp/manifest.d.ts +0 -11
- package/dist/dlp/manifest.d.ts.map +0 -1
- package/dist/dlp/manifest.js +0 -14
- package/dist/dlp/manifest.js.map +0 -1
- package/dist/dlp/payload.d.ts +0 -6
- package/dist/dlp/payload.d.ts.map +0 -1
- package/dist/dlp/payload.js +0 -62
- package/dist/dlp/payload.js.map +0 -1
- package/dist/dlp/rng.d.ts +0 -9
- package/dist/dlp/rng.d.ts.map +0 -1
- package/dist/dlp/rng.js +0 -28
- package/dist/dlp/rng.js.map +0 -1
- package/dist/dlp/types.d.ts +0 -26
- package/dist/dlp/types.d.ts.map +0 -1
- package/dist/dlp/types.js +0 -2
- package/dist/dlp/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/llm/prompts/analyze-results.d.ts +0 -3
- package/dist/llm/prompts/analyze-results.d.ts.map +0 -1
- package/dist/llm/prompts/analyze-results.js +0 -52
- package/dist/llm/prompts/analyze-results.js.map +0 -1
- package/dist/llm/prompts/generate-companion.d.ts +0 -3
- package/dist/llm/prompts/generate-companion.d.ts.map +0 -1
- package/dist/llm/prompts/generate-companion.js +0 -42
- package/dist/llm/prompts/generate-companion.js.map +0 -1
- package/dist/llm/prompts/generate-tests.d.ts +0 -3
- package/dist/llm/prompts/generate-tests.d.ts.map +0 -1
- package/dist/llm/prompts/generate-tests.js +0 -46
- package/dist/llm/prompts/generate-tests.js.map +0 -1
- package/dist/llm/prompts/generate-topic.d.ts +0 -4
- package/dist/llm/prompts/generate-topic.d.ts.map +0 -1
- package/dist/llm/prompts/generate-topic.js +0 -41
- package/dist/llm/prompts/generate-topic.js.map +0 -1
- package/dist/llm/prompts/improve-topic.d.ts +0 -3
- package/dist/llm/prompts/improve-topic.d.ts.map +0 -1
- package/dist/llm/prompts/improve-topic.js +0 -77
- package/dist/llm/prompts/improve-topic.js.map +0 -1
- package/dist/llm/prompts/simplify-topic.d.ts +0 -3
- package/dist/llm/prompts/simplify-topic.d.ts.map +0 -1
- package/dist/llm/prompts/simplify-topic.js +0 -50
- package/dist/llm/prompts/simplify-topic.js.map +0 -1
- package/dist/llm/provider.d.ts +0 -21
- package/dist/llm/provider.d.ts.map +0 -1
- package/dist/llm/provider.js +0 -76
- package/dist/llm/provider.js.map +0 -1
- package/dist/llm/schemas.d.ts +0 -97
- package/dist/llm/schemas.d.ts.map +0 -1
- package/dist/llm/schemas.js +0 -24
- package/dist/llm/schemas.js.map +0 -1
- package/dist/llm/service.d.ts +0 -34
- package/dist/llm/service.d.ts.map +0 -1
- package/dist/llm/service.js +0 -268
- package/dist/llm/service.js.map +0 -1
- package/dist/llm/types.d.ts +0 -18
- package/dist/llm/types.d.ts.map +0 -1
- package/dist/llm/types.js +0 -2
- package/dist/llm/types.js.map +0 -1
- package/dist/report/html.d.ts +0 -6
- package/dist/report/html.d.ts.map +0 -1
- package/dist/report/html.js +0 -200
- package/dist/report/html.js.map +0 -1
- package/dist/report/json.d.ts +0 -11
- package/dist/report/json.d.ts.map +0 -1
- package/dist/report/json.js +0 -74
- package/dist/report/json.js.map +0 -1
- package/dist/report/types.d.ts +0 -58
- package/dist/report/types.d.ts.map +0 -1
- package/dist/report/types.js +0 -5
- package/dist/report/types.js.map +0 -1
|
@@ -0,0 +1,1752 @@
|
|
|
1
|
+
// src/airs/management.ts
|
|
2
|
+
import {
|
|
3
|
+
ManagementClient
|
|
4
|
+
} from "@cdot65/prisma-airs-sdk";
|
|
5
|
+
var SdkManagementService = class {
|
|
6
|
+
client;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
this.client = getOrCreateManagementClient(opts);
|
|
9
|
+
}
|
|
10
|
+
async createTopic(request) {
|
|
11
|
+
return this.client.topics.create(request);
|
|
12
|
+
}
|
|
13
|
+
async updateTopic(topicId, request) {
|
|
14
|
+
return this.client.topics.update(topicId, request);
|
|
15
|
+
}
|
|
16
|
+
async deleteTopic(topicId) {
|
|
17
|
+
await this.client.topics.delete(topicId);
|
|
18
|
+
}
|
|
19
|
+
async forceDeleteTopic(topicId, updatedBy) {
|
|
20
|
+
const response = await this.client.topics.forceDelete(topicId, updatedBy);
|
|
21
|
+
return { message: response.message };
|
|
22
|
+
}
|
|
23
|
+
async listTopics() {
|
|
24
|
+
const response = await this.client.topics.list();
|
|
25
|
+
return response.custom_topics;
|
|
26
|
+
}
|
|
27
|
+
async getTopic(topicId) {
|
|
28
|
+
const topics = await this.listTopics();
|
|
29
|
+
const topic = topics.find((t) => t.topic_id === topicId);
|
|
30
|
+
if (!topic) throw new Error(`Topic ${topicId} not found`);
|
|
31
|
+
return topic;
|
|
32
|
+
}
|
|
33
|
+
async getTopicByName(topicName) {
|
|
34
|
+
const topics = await this.listTopics();
|
|
35
|
+
const topic = topics.find((t) => t.topic_name === topicName);
|
|
36
|
+
if (!topic) throw new Error(`Topic "${topicName}" not found`);
|
|
37
|
+
return topic;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets a single custom topic on a profile's topic-guardrails config.
|
|
41
|
+
* Delegates to {@link assignTopicsToProfile} for backward compatibility.
|
|
42
|
+
*/
|
|
43
|
+
async assignTopicToProfile(profileName, topicId, topicName, action) {
|
|
44
|
+
return this.assignTopicsToProfile(profileName, [{ topicId, topicName, action }]);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sets one or more custom topics on a profile's topic-guardrails config.
|
|
48
|
+
* Replaces any existing topics — previous runs' stale topics are cleared.
|
|
49
|
+
* Groups topics by action; skips empty action groups (AIRS rejects them).
|
|
50
|
+
*
|
|
51
|
+
* CRITICAL: Each topic entry MUST include the current `revision` number.
|
|
52
|
+
* AIRS pins topic content to the revision specified in the profile — omitting
|
|
53
|
+
* it defaults to revision 0 (original content), not the latest.
|
|
54
|
+
*/
|
|
55
|
+
async assignTopicsToProfile(profileName, topics, guardrailAction) {
|
|
56
|
+
const { ai_profiles } = await this.client.profiles.list();
|
|
57
|
+
const profile = ai_profiles.find((p) => p.profile_name === profileName);
|
|
58
|
+
if (!profile?.profile_id) {
|
|
59
|
+
throw new Error(`Profile "${profileName}" not found`);
|
|
60
|
+
}
|
|
61
|
+
const allTopics = await this.listTopics();
|
|
62
|
+
const revisionMap = new Map(allTopics.map((t) => [t.topic_id, t.revision ?? 0]));
|
|
63
|
+
const policy = JSON.parse(JSON.stringify(profile.policy ?? {}));
|
|
64
|
+
const aiProfiles = policy["ai-security-profiles"] ?? [
|
|
65
|
+
{ "model-type": "default", "model-configuration": {} }
|
|
66
|
+
];
|
|
67
|
+
const modelConfig = aiProfiles[0]?.["model-configuration"] ?? {};
|
|
68
|
+
const modelProtection = modelConfig["model-protection"] ?? [];
|
|
69
|
+
let topicGuardrails = modelProtection.find((mp) => mp.name === "topic-guardrails");
|
|
70
|
+
if (!topicGuardrails) {
|
|
71
|
+
topicGuardrails = {
|
|
72
|
+
action: "block",
|
|
73
|
+
name: "topic-guardrails",
|
|
74
|
+
options: [],
|
|
75
|
+
"topic-list": []
|
|
76
|
+
};
|
|
77
|
+
modelProtection.push(topicGuardrails);
|
|
78
|
+
}
|
|
79
|
+
topicGuardrails.action = guardrailAction ?? "block";
|
|
80
|
+
const byAction = /* @__PURE__ */ new Map();
|
|
81
|
+
for (const t of topics) {
|
|
82
|
+
const group = byAction.get(t.action) ?? [];
|
|
83
|
+
group.push({
|
|
84
|
+
topic_id: t.topicId,
|
|
85
|
+
topic_name: t.topicName,
|
|
86
|
+
revision: revisionMap.get(t.topicId) ?? 0
|
|
87
|
+
});
|
|
88
|
+
byAction.set(t.action, group);
|
|
89
|
+
}
|
|
90
|
+
const topicList = [];
|
|
91
|
+
for (const [action, group] of byAction) {
|
|
92
|
+
topicList.push({ action, topic: group });
|
|
93
|
+
}
|
|
94
|
+
topicGuardrails["topic-list"] = topicList;
|
|
95
|
+
modelConfig["model-protection"] = modelProtection;
|
|
96
|
+
aiProfiles[0]["model-configuration"] = modelConfig;
|
|
97
|
+
policy["ai-security-profiles"] = aiProfiles;
|
|
98
|
+
await this.client.profiles.update(profile.profile_id, {
|
|
99
|
+
profile_name: profile.profile_name,
|
|
100
|
+
active: profile.active,
|
|
101
|
+
policy
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async getProfileTopics(profileName) {
|
|
105
|
+
const { ai_profiles } = await this.client.profiles.list();
|
|
106
|
+
const profile = ai_profiles.find((p) => p.profile_name === profileName);
|
|
107
|
+
if (!profile?.profile_id) {
|
|
108
|
+
throw new Error(`Profile "${profileName}" not found`);
|
|
109
|
+
}
|
|
110
|
+
const policy = profile.policy ?? {};
|
|
111
|
+
const aiProfiles = policy["ai-security-profiles"] ?? [];
|
|
112
|
+
const modelConfig = aiProfiles[0]?.["model-configuration"] ?? {};
|
|
113
|
+
const modelProtection = modelConfig["model-protection"] ?? [];
|
|
114
|
+
const topicGuardrails = modelProtection.find((mp) => mp.name === "topic-guardrails");
|
|
115
|
+
if (!topicGuardrails) return [];
|
|
116
|
+
const topicList = topicGuardrails["topic-list"] ?? [];
|
|
117
|
+
const topicRefs = [];
|
|
118
|
+
for (const entry of topicList) {
|
|
119
|
+
for (const t of entry.topic ?? []) {
|
|
120
|
+
topicRefs.push({
|
|
121
|
+
topicId: t.topic_id,
|
|
122
|
+
topicName: t.topic_name,
|
|
123
|
+
action: entry.action
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (topicRefs.length === 0) return [];
|
|
128
|
+
const allTopics = await this.listTopics();
|
|
129
|
+
const topicMap = new Map(allTopics.map((t) => [t.topic_id, t]));
|
|
130
|
+
return topicRefs.map((ref) => {
|
|
131
|
+
const full = topicMap.get(ref.topicId);
|
|
132
|
+
return {
|
|
133
|
+
topicId: ref.topicId,
|
|
134
|
+
topicName: ref.topicName,
|
|
135
|
+
action: ref.action,
|
|
136
|
+
description: full?.description ?? "",
|
|
137
|
+
examples: full?.examples ?? []
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
// Profile CRUD
|
|
143
|
+
// -------------------------------------------------------------------------
|
|
144
|
+
normalizeProfile(p) {
|
|
145
|
+
return {
|
|
146
|
+
profileId: p.profile_id,
|
|
147
|
+
profileName: p.profile_name,
|
|
148
|
+
revision: p.revision,
|
|
149
|
+
active: p.active,
|
|
150
|
+
createdBy: p.created_by,
|
|
151
|
+
updatedBy: p.updated_by,
|
|
152
|
+
lastModifiedTs: p.last_modified_ts,
|
|
153
|
+
policy: p.policy
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async getProfile(profileId) {
|
|
157
|
+
const response = await this.client.profiles.get(profileId);
|
|
158
|
+
return this.normalizeProfile(response);
|
|
159
|
+
}
|
|
160
|
+
async getProfileByName(profileName) {
|
|
161
|
+
const response = await this.client.profiles.getByName(profileName);
|
|
162
|
+
return this.normalizeProfile(response);
|
|
163
|
+
}
|
|
164
|
+
async listProfiles(opts) {
|
|
165
|
+
const response = await this.client.profiles.list(opts);
|
|
166
|
+
return {
|
|
167
|
+
profiles: (response.ai_profiles ?? []).map(
|
|
168
|
+
(p) => this.normalizeProfile(p)
|
|
169
|
+
),
|
|
170
|
+
nextOffset: response.next_offset
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async createProfile(request) {
|
|
174
|
+
const response = await this.client.profiles.create(request);
|
|
175
|
+
return this.normalizeProfile(response);
|
|
176
|
+
}
|
|
177
|
+
async updateProfile(profileId, request) {
|
|
178
|
+
const response = await this.client.profiles.update(profileId, request);
|
|
179
|
+
return this.normalizeProfile(response);
|
|
180
|
+
}
|
|
181
|
+
async deleteProfile(profileId) {
|
|
182
|
+
const response = await this.client.profiles.delete(profileId);
|
|
183
|
+
return { message: response.message };
|
|
184
|
+
}
|
|
185
|
+
async forceDeleteProfile(profileId, updatedBy) {
|
|
186
|
+
const response = await this.client.profiles.forceDelete(profileId, updatedBy);
|
|
187
|
+
return { message: response.message };
|
|
188
|
+
}
|
|
189
|
+
// -------------------------------------------------------------------------
|
|
190
|
+
// API Keys
|
|
191
|
+
// -------------------------------------------------------------------------
|
|
192
|
+
normalizeApiKey(k) {
|
|
193
|
+
return {
|
|
194
|
+
id: k.id ?? k.api_key_id ?? "",
|
|
195
|
+
name: k.name ?? k.api_key_name ?? "",
|
|
196
|
+
apiKey: k.api_key,
|
|
197
|
+
last8: k.api_key_last8,
|
|
198
|
+
createdAt: k.created_at,
|
|
199
|
+
expiresAt: k.expires_at
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
async listApiKeys(opts) {
|
|
203
|
+
const response = await this.client.apiKeys.list(opts);
|
|
204
|
+
const raw = response;
|
|
205
|
+
const keys = raw.api_keys ?? [];
|
|
206
|
+
return {
|
|
207
|
+
apiKeys: keys.map((k) => this.normalizeApiKey(k)),
|
|
208
|
+
nextOffset: raw.next_offset
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async createApiKey(request) {
|
|
212
|
+
const response = await this.client.apiKeys.create(request);
|
|
213
|
+
return this.normalizeApiKey(response);
|
|
214
|
+
}
|
|
215
|
+
async regenerateApiKey(apiKeyId, request) {
|
|
216
|
+
const response = await this.client.apiKeys.regenerate(apiKeyId, request);
|
|
217
|
+
return this.normalizeApiKey(response);
|
|
218
|
+
}
|
|
219
|
+
async deleteApiKey(apiKeyName, updatedBy) {
|
|
220
|
+
const response = await this.client.apiKeys.delete(apiKeyName, updatedBy);
|
|
221
|
+
return { message: response.message };
|
|
222
|
+
}
|
|
223
|
+
// -------------------------------------------------------------------------
|
|
224
|
+
// Customer Apps
|
|
225
|
+
// -------------------------------------------------------------------------
|
|
226
|
+
normalizeCustomerApp(a) {
|
|
227
|
+
return {
|
|
228
|
+
id: a.customer_app_id ?? a.id,
|
|
229
|
+
name: a.app_name ?? a.name ?? "",
|
|
230
|
+
description: a.description,
|
|
231
|
+
raw: a
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async listCustomerApps(opts) {
|
|
235
|
+
const response = await this.client.customerApps.list(opts);
|
|
236
|
+
const raw = response;
|
|
237
|
+
const apps = raw.customer_apps ?? [];
|
|
238
|
+
return {
|
|
239
|
+
apps: apps.map((a) => this.normalizeCustomerApp(a)),
|
|
240
|
+
nextOffset: raw.next_offset
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
async getCustomerApp(appName) {
|
|
244
|
+
const response = await this.client.customerApps.get(appName);
|
|
245
|
+
return this.normalizeCustomerApp(response);
|
|
246
|
+
}
|
|
247
|
+
async updateCustomerApp(appId, request) {
|
|
248
|
+
const response = await this.client.customerApps.update(appId, request);
|
|
249
|
+
return this.normalizeCustomerApp(response);
|
|
250
|
+
}
|
|
251
|
+
async deleteCustomerApp(appName, updatedBy) {
|
|
252
|
+
const response = await this.client.customerApps.delete(appName, updatedBy);
|
|
253
|
+
return this.normalizeCustomerApp(response);
|
|
254
|
+
}
|
|
255
|
+
async listConsumptionApps(opts) {
|
|
256
|
+
const response = await this.client.dashboard.applicationsOverview({
|
|
257
|
+
limit: opts?.limit ?? 100,
|
|
258
|
+
offset: opts?.offset ?? 0
|
|
259
|
+
});
|
|
260
|
+
return (response.items ?? []).filter(
|
|
261
|
+
(item) => typeof item.id === "string" && typeof item.name === "string"
|
|
262
|
+
).map((item) => ({
|
|
263
|
+
appId: item.id,
|
|
264
|
+
appName: item.name,
|
|
265
|
+
cloud: item.cloud ?? void 0,
|
|
266
|
+
source: item.source ?? void 0
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
async getCustomerAppConsumption(appName, opts) {
|
|
270
|
+
const dashboardApps = await this.listConsumptionApps({ limit: 100 });
|
|
271
|
+
const target = dashboardApps.find((a) => a.appName === appName);
|
|
272
|
+
const appId = target?.appId;
|
|
273
|
+
if (!appId) {
|
|
274
|
+
const sample = dashboardApps.slice(0, 5).map((a) => `"${a.appName}"`).join(", ");
|
|
275
|
+
const more = dashboardApps.length > 5 ? `, ... (${dashboardApps.length} total)` : "";
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Dashboard application not found: "${appName}". Available (as shown in SCM AI Applications view): ${sample}${more}. Note: the name to use is the literal value your integration sends in scan metadata.app_name (which may differ from the SCM application name).`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
const timeInterval = opts?.timeInterval ?? 30;
|
|
281
|
+
const query = { appId, appName, timeInterval };
|
|
282
|
+
const [overview, breakdown] = await Promise.all([
|
|
283
|
+
this.client.dashboard.application(query),
|
|
284
|
+
this.client.dashboard.applicationViolationBreakdown(query)
|
|
285
|
+
]);
|
|
286
|
+
const ts = overview.token_stats ?? {};
|
|
287
|
+
const ss = overview.session_stats ?? {};
|
|
288
|
+
const detectors = (breakdown.detection_type_violation_breakdown ?? []).map((entry) => {
|
|
289
|
+
const vb = entry.violation_breakdown ?? {};
|
|
290
|
+
return {
|
|
291
|
+
type: entry.detection_type ?? "unknown",
|
|
292
|
+
critical: vb.critical ?? 0,
|
|
293
|
+
high: vb.high ?? 0,
|
|
294
|
+
medium: vb.medium ?? 0,
|
|
295
|
+
low: vb.low ?? 0,
|
|
296
|
+
total: vb.total ?? 0
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
return {
|
|
300
|
+
appId,
|
|
301
|
+
appName,
|
|
302
|
+
cloud: overview.cloud ?? void 0,
|
|
303
|
+
source: overview.source ?? void 0,
|
|
304
|
+
monitoringSince: overview.created_at ?? void 0,
|
|
305
|
+
profiles: overview.profiles ?? [],
|
|
306
|
+
tokens: {
|
|
307
|
+
dailyAverage: ts.average_daily_tokens ?? void 0,
|
|
308
|
+
dailyAverageScale: ts.average_daily_tokens_scale ?? void 0,
|
|
309
|
+
monthlyTotal: ts.monthly_total_tokens ?? void 0,
|
|
310
|
+
monthlyTotalScale: ts.monthly_total_tokens_scale ?? void 0
|
|
311
|
+
},
|
|
312
|
+
sessions: {
|
|
313
|
+
total: ss.total ?? 0,
|
|
314
|
+
violating: ss.violating ?? 0
|
|
315
|
+
},
|
|
316
|
+
detectors,
|
|
317
|
+
totalViolating: breakdown.total_violating ?? 0
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// -------------------------------------------------------------------------
|
|
321
|
+
// Deployment Profiles (read-only)
|
|
322
|
+
// -------------------------------------------------------------------------
|
|
323
|
+
async listDeploymentProfiles(opts) {
|
|
324
|
+
const response = await this.client.deploymentProfiles.list(opts);
|
|
325
|
+
const raw = response;
|
|
326
|
+
const profiles = raw.deployment_profiles ?? [];
|
|
327
|
+
return profiles.map((p) => ({ raw: p }));
|
|
328
|
+
}
|
|
329
|
+
// -------------------------------------------------------------------------
|
|
330
|
+
// Scan Logs
|
|
331
|
+
// -------------------------------------------------------------------------
|
|
332
|
+
async queryScanLogs(opts) {
|
|
333
|
+
const response = await this.client.scanLogs.query({
|
|
334
|
+
time_interval: opts.timeInterval,
|
|
335
|
+
time_unit: opts.timeUnit,
|
|
336
|
+
pageNumber: opts.pageNumber,
|
|
337
|
+
pageSize: opts.pageSize,
|
|
338
|
+
filter: opts.filter,
|
|
339
|
+
page_token: opts.pageToken
|
|
340
|
+
});
|
|
341
|
+
const raw = response;
|
|
342
|
+
const dashboard = raw.scan_result_for_dashboard ?? {};
|
|
343
|
+
const entries = dashboard.scan_result_entries ?? raw.results ?? [];
|
|
344
|
+
return {
|
|
345
|
+
results: entries,
|
|
346
|
+
pageToken: raw.page_token,
|
|
347
|
+
raw
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
var _sharedClient;
|
|
352
|
+
function getOrCreateManagementClient(opts) {
|
|
353
|
+
if (!_sharedClient) {
|
|
354
|
+
_sharedClient = new ManagementClient(opts);
|
|
355
|
+
}
|
|
356
|
+
return _sharedClient;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/airs/modelsecurity.ts
|
|
360
|
+
import { ModelSecurityClient } from "@cdot65/prisma-airs-sdk";
|
|
361
|
+
function normalizeGroup(raw) {
|
|
362
|
+
return {
|
|
363
|
+
uuid: raw.uuid,
|
|
364
|
+
name: raw.name,
|
|
365
|
+
description: raw.description,
|
|
366
|
+
sourceType: raw.source_type,
|
|
367
|
+
state: raw.state,
|
|
368
|
+
createdAt: raw.created_at,
|
|
369
|
+
updatedAt: raw.updated_at
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function normalizeRuleInstance(raw) {
|
|
373
|
+
return {
|
|
374
|
+
uuid: raw.uuid,
|
|
375
|
+
securityGroupUuid: raw.security_group_uuid,
|
|
376
|
+
securityRuleUuid: raw.security_rule_uuid,
|
|
377
|
+
state: raw.state,
|
|
378
|
+
createdAt: raw.created_at,
|
|
379
|
+
updatedAt: raw.updated_at,
|
|
380
|
+
rule: raw.rule,
|
|
381
|
+
fieldValues: raw.field_values ?? {}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function normalizeRule(raw) {
|
|
385
|
+
const editableFields = raw.editable_fields ?? [];
|
|
386
|
+
return {
|
|
387
|
+
uuid: raw.uuid,
|
|
388
|
+
name: raw.name,
|
|
389
|
+
description: raw.description,
|
|
390
|
+
ruleType: raw.rule_type,
|
|
391
|
+
compatibleSources: raw.compatible_sources,
|
|
392
|
+
defaultState: raw.default_state,
|
|
393
|
+
remediation: raw.remediation,
|
|
394
|
+
editableFields: editableFields.map(
|
|
395
|
+
(f) => ({
|
|
396
|
+
attributeName: f.attribute_name,
|
|
397
|
+
type: f.type,
|
|
398
|
+
displayName: f.display_name,
|
|
399
|
+
displayType: f.display_type,
|
|
400
|
+
description: f.description,
|
|
401
|
+
dropdownValues: f.dropdown_values
|
|
402
|
+
})
|
|
403
|
+
),
|
|
404
|
+
constantValues: raw.constant_values ?? {},
|
|
405
|
+
defaultValues: raw.default_values ?? {}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function normalizeScan(raw) {
|
|
409
|
+
const summary = raw.eval_summary;
|
|
410
|
+
return {
|
|
411
|
+
uuid: raw.uuid,
|
|
412
|
+
evalOutcome: raw.eval_outcome ?? "",
|
|
413
|
+
modelUri: raw.model_uri ?? "",
|
|
414
|
+
scanOrigin: raw.scan_origin ?? "",
|
|
415
|
+
sourceType: raw.source_type ?? "",
|
|
416
|
+
securityGroupName: raw.security_group_name ?? "",
|
|
417
|
+
evalSummary: summary ? {
|
|
418
|
+
rulesFailed: summary.rules_failed ?? 0,
|
|
419
|
+
rulesPassed: summary.rules_passed ?? 0,
|
|
420
|
+
totalRules: summary.total_rules ?? 0
|
|
421
|
+
} : null,
|
|
422
|
+
createdAt: raw.created_at,
|
|
423
|
+
updatedAt: raw.updated_at,
|
|
424
|
+
labels: raw.labels ?? []
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function normalizeEvaluation(raw) {
|
|
428
|
+
return {
|
|
429
|
+
uuid: raw.uuid,
|
|
430
|
+
result: raw.result,
|
|
431
|
+
violationCount: raw.violation_count ?? 0,
|
|
432
|
+
ruleInstanceUuid: raw.rule_instance_uuid ?? "",
|
|
433
|
+
ruleName: raw.rule_name,
|
|
434
|
+
ruleDescription: raw.rule_description ?? "",
|
|
435
|
+
ruleInstanceState: raw.rule_instance_state ?? ""
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function normalizeViolation(raw) {
|
|
439
|
+
return {
|
|
440
|
+
uuid: raw.uuid,
|
|
441
|
+
description: raw.description,
|
|
442
|
+
threat: raw.threat ?? "",
|
|
443
|
+
threatDescription: raw.threat_description ?? "",
|
|
444
|
+
file: raw.file ?? "",
|
|
445
|
+
ruleName: raw.rule_name,
|
|
446
|
+
ruleDescription: raw.rule_description ?? "",
|
|
447
|
+
ruleInstanceState: raw.rule_instance_state ?? ""
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function normalizeFile(raw) {
|
|
451
|
+
return {
|
|
452
|
+
uuid: raw.uuid,
|
|
453
|
+
path: raw.path ?? "",
|
|
454
|
+
type: raw.type,
|
|
455
|
+
formats: raw.formats ?? [],
|
|
456
|
+
result: raw.result
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
var SdkModelSecurityService = class {
|
|
460
|
+
client;
|
|
461
|
+
constructor(opts) {
|
|
462
|
+
this.client = new ModelSecurityClient(opts);
|
|
463
|
+
}
|
|
464
|
+
// -----------------------------------------------------------------------
|
|
465
|
+
// Security Groups
|
|
466
|
+
// -----------------------------------------------------------------------
|
|
467
|
+
async listGroups(opts) {
|
|
468
|
+
const sdkOpts = {};
|
|
469
|
+
if (opts?.sourceTypes) sdkOpts.source_types = opts.sourceTypes;
|
|
470
|
+
if (opts?.searchQuery) sdkOpts.search_query = opts.searchQuery;
|
|
471
|
+
if (opts?.sortField) sdkOpts.sort_field = opts.sortField;
|
|
472
|
+
if (opts?.sortDir) sdkOpts.sort_dir = opts.sortDir;
|
|
473
|
+
if (opts?.enabledRules) sdkOpts.enabled_rules = opts.enabledRules;
|
|
474
|
+
if (opts?.skip !== void 0) sdkOpts.skip = opts.skip;
|
|
475
|
+
if (opts?.limit !== void 0) sdkOpts.limit = opts.limit;
|
|
476
|
+
const response = await this.client.securityGroups.list(sdkOpts);
|
|
477
|
+
const raw = response;
|
|
478
|
+
return {
|
|
479
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
480
|
+
groups: raw.security_groups.map(normalizeGroup)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
async getGroup(uuid) {
|
|
484
|
+
const response = await this.client.securityGroups.get(uuid);
|
|
485
|
+
return normalizeGroup(response);
|
|
486
|
+
}
|
|
487
|
+
async createGroup(request) {
|
|
488
|
+
const sdkRequest = {
|
|
489
|
+
name: request.name,
|
|
490
|
+
source_type: request.sourceType
|
|
491
|
+
};
|
|
492
|
+
if (request.description !== void 0) sdkRequest.description = request.description;
|
|
493
|
+
if (request.ruleConfigurations) sdkRequest.rule_configurations = request.ruleConfigurations;
|
|
494
|
+
const response = await this.client.securityGroups.create(sdkRequest);
|
|
495
|
+
return normalizeGroup(response);
|
|
496
|
+
}
|
|
497
|
+
async updateGroup(uuid, request) {
|
|
498
|
+
const response = await this.client.securityGroups.update(uuid, request);
|
|
499
|
+
return normalizeGroup(response);
|
|
500
|
+
}
|
|
501
|
+
async deleteGroup(uuid) {
|
|
502
|
+
await this.client.securityGroups.delete(uuid);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Delete a group, then verify whether it is actually gone. The API soft/async-deletes
|
|
506
|
+
* security groups — a successful DELETE does not immediately remove the resource, so a
|
|
507
|
+
* follow-up `get` may still return it as ACTIVE (see prisma-airs-cli#239). The SDK delete
|
|
508
|
+
* returns void, so re-reading is the only way to report the real outcome rather than
|
|
509
|
+
* claiming an unconditional success. Returns `{ confirmed: true }` when the group no longer
|
|
510
|
+
* resolves, or `{ confirmed: false, state }` when it still does.
|
|
511
|
+
*/
|
|
512
|
+
async deleteGroupAndVerify(uuid) {
|
|
513
|
+
await this.deleteGroup(uuid);
|
|
514
|
+
try {
|
|
515
|
+
const group = await this.getGroup(uuid);
|
|
516
|
+
return { confirmed: false, state: group.state };
|
|
517
|
+
} catch {
|
|
518
|
+
return { confirmed: true };
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// -----------------------------------------------------------------------
|
|
522
|
+
// Rule Instances
|
|
523
|
+
// -----------------------------------------------------------------------
|
|
524
|
+
async listRuleInstances(groupUuid, opts) {
|
|
525
|
+
const sdkOpts = {};
|
|
526
|
+
if (opts?.securityRuleUuid) sdkOpts.security_rule_uuid = opts.securityRuleUuid;
|
|
527
|
+
if (opts?.state) sdkOpts.state = opts.state;
|
|
528
|
+
if (opts?.skip !== void 0) sdkOpts.skip = opts.skip;
|
|
529
|
+
if (opts?.limit !== void 0) sdkOpts.limit = opts.limit;
|
|
530
|
+
const response = await this.client.securityGroups.listRuleInstances(
|
|
531
|
+
groupUuid,
|
|
532
|
+
sdkOpts
|
|
533
|
+
);
|
|
534
|
+
const raw = response;
|
|
535
|
+
return {
|
|
536
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
537
|
+
ruleInstances: raw.rule_instances.map(normalizeRuleInstance)
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
async getRuleInstance(groupUuid, instanceUuid) {
|
|
541
|
+
const response = await this.client.securityGroups.getRuleInstance(groupUuid, instanceUuid);
|
|
542
|
+
return normalizeRuleInstance(response);
|
|
543
|
+
}
|
|
544
|
+
async updateRuleInstance(groupUuid, instanceUuid, request) {
|
|
545
|
+
const sdkRequest = {
|
|
546
|
+
security_group_uuid: groupUuid
|
|
547
|
+
};
|
|
548
|
+
if (request.state !== void 0) sdkRequest.state = request.state;
|
|
549
|
+
if (request.fieldValues !== void 0) sdkRequest.field_values = request.fieldValues;
|
|
550
|
+
const response = await this.client.securityGroups.updateRuleInstance(
|
|
551
|
+
groupUuid,
|
|
552
|
+
instanceUuid,
|
|
553
|
+
sdkRequest
|
|
554
|
+
);
|
|
555
|
+
return normalizeRuleInstance(response);
|
|
556
|
+
}
|
|
557
|
+
// -----------------------------------------------------------------------
|
|
558
|
+
// Security Rules
|
|
559
|
+
// -----------------------------------------------------------------------
|
|
560
|
+
async listRules(opts) {
|
|
561
|
+
const sdkOpts = {};
|
|
562
|
+
if (opts?.sourceType) sdkOpts.source_type = opts.sourceType;
|
|
563
|
+
if (opts?.searchQuery) sdkOpts.search_query = opts.searchQuery;
|
|
564
|
+
if (opts?.skip !== void 0) sdkOpts.skip = opts.skip;
|
|
565
|
+
if (opts?.limit !== void 0) sdkOpts.limit = opts.limit;
|
|
566
|
+
const response = await this.client.securityRules.list(sdkOpts);
|
|
567
|
+
const raw = response;
|
|
568
|
+
return {
|
|
569
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
570
|
+
rules: raw.rules.map(normalizeRule)
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
async getRule(uuid) {
|
|
574
|
+
const response = await this.client.securityRules.get(uuid);
|
|
575
|
+
return normalizeRule(response);
|
|
576
|
+
}
|
|
577
|
+
// -----------------------------------------------------------------------
|
|
578
|
+
// Scans
|
|
579
|
+
// -----------------------------------------------------------------------
|
|
580
|
+
async createScan(request) {
|
|
581
|
+
const response = await this.client.scans.create(request);
|
|
582
|
+
return normalizeScan(response);
|
|
583
|
+
}
|
|
584
|
+
async listScans(opts) {
|
|
585
|
+
const sdkOpts = {};
|
|
586
|
+
if (opts?.evalOutcome) sdkOpts.eval_outcome = opts.evalOutcome;
|
|
587
|
+
if (opts?.sourceType) sdkOpts.source_type = opts.sourceType;
|
|
588
|
+
if (opts?.scanOrigin) sdkOpts.scan_origin = opts.scanOrigin;
|
|
589
|
+
if (opts?.search) sdkOpts.search = opts.search;
|
|
590
|
+
if (opts?.skip !== void 0) sdkOpts.skip = opts.skip;
|
|
591
|
+
if (opts?.limit !== void 0) sdkOpts.limit = opts.limit;
|
|
592
|
+
const response = await this.client.scans.list(sdkOpts);
|
|
593
|
+
const raw = response;
|
|
594
|
+
return {
|
|
595
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
596
|
+
scans: raw.scans.map(normalizeScan)
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
async getScan(uuid) {
|
|
600
|
+
const response = await this.client.scans.get(uuid);
|
|
601
|
+
return normalizeScan(response);
|
|
602
|
+
}
|
|
603
|
+
// -----------------------------------------------------------------------
|
|
604
|
+
// Evaluations
|
|
605
|
+
// -----------------------------------------------------------------------
|
|
606
|
+
async getEvaluations(scanUuid, opts) {
|
|
607
|
+
const response = await this.client.scans.getEvaluations(scanUuid, opts);
|
|
608
|
+
const raw = response;
|
|
609
|
+
return {
|
|
610
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
611
|
+
evaluations: raw.evaluations.map(normalizeEvaluation)
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
async getEvaluation(uuid) {
|
|
615
|
+
const response = await this.client.scans.getEvaluation(uuid);
|
|
616
|
+
return normalizeEvaluation(response);
|
|
617
|
+
}
|
|
618
|
+
// -----------------------------------------------------------------------
|
|
619
|
+
// Violations
|
|
620
|
+
// -----------------------------------------------------------------------
|
|
621
|
+
async getViolations(scanUuid, opts) {
|
|
622
|
+
const response = await this.client.scans.getViolations(scanUuid, opts);
|
|
623
|
+
const raw = response;
|
|
624
|
+
return {
|
|
625
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
626
|
+
violations: raw.violations.map(normalizeViolation)
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
async getViolation(uuid) {
|
|
630
|
+
const response = await this.client.scans.getViolation(uuid);
|
|
631
|
+
return normalizeViolation(response);
|
|
632
|
+
}
|
|
633
|
+
// -----------------------------------------------------------------------
|
|
634
|
+
// Files
|
|
635
|
+
// -----------------------------------------------------------------------
|
|
636
|
+
async getFiles(scanUuid, opts) {
|
|
637
|
+
const response = await this.client.scans.getFiles(scanUuid, opts);
|
|
638
|
+
const raw = response;
|
|
639
|
+
return {
|
|
640
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
641
|
+
files: raw.files.map(normalizeFile)
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
// -----------------------------------------------------------------------
|
|
645
|
+
// Labels
|
|
646
|
+
// -----------------------------------------------------------------------
|
|
647
|
+
async addLabels(scanUuid, labels) {
|
|
648
|
+
await this.client.scans.addLabels(scanUuid, { labels });
|
|
649
|
+
}
|
|
650
|
+
async setLabels(scanUuid, labels) {
|
|
651
|
+
await this.client.scans.setLabels(scanUuid, { labels });
|
|
652
|
+
}
|
|
653
|
+
async deleteLabels(scanUuid, keys) {
|
|
654
|
+
await this.client.scans.deleteLabels(scanUuid, keys);
|
|
655
|
+
}
|
|
656
|
+
async getLabelKeys(opts) {
|
|
657
|
+
const response = await this.client.scans.getLabelKeys(opts);
|
|
658
|
+
const raw = response;
|
|
659
|
+
return {
|
|
660
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
661
|
+
keys: raw.keys
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
async getLabelValues(key, opts) {
|
|
665
|
+
const response = await this.client.scans.getLabelValues(key, opts);
|
|
666
|
+
const raw = response;
|
|
667
|
+
return {
|
|
668
|
+
totalItems: raw.pagination.total_items ?? 0,
|
|
669
|
+
values: raw.values
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
// -----------------------------------------------------------------------
|
|
673
|
+
// PyPI Auth
|
|
674
|
+
// -----------------------------------------------------------------------
|
|
675
|
+
async getPyPIAuth() {
|
|
676
|
+
const response = await this.client.getPyPIAuth();
|
|
677
|
+
const raw = response;
|
|
678
|
+
return {
|
|
679
|
+
url: raw.url,
|
|
680
|
+
expiresAt: raw.expires_at
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// src/airs/promptsets.ts
|
|
686
|
+
import { RedTeamClient } from "@cdot65/prisma-airs-sdk";
|
|
687
|
+
function normalizePromptSet(raw) {
|
|
688
|
+
return {
|
|
689
|
+
uuid: raw.uuid,
|
|
690
|
+
name: raw.name,
|
|
691
|
+
active: raw.active,
|
|
692
|
+
archive: raw.archive,
|
|
693
|
+
description: raw.description,
|
|
694
|
+
createdAt: raw.created_at,
|
|
695
|
+
updatedAt: raw.updated_at
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function normalizePrompt(raw) {
|
|
699
|
+
return {
|
|
700
|
+
uuid: raw.uuid,
|
|
701
|
+
prompt: raw.prompt,
|
|
702
|
+
goal: raw.goal,
|
|
703
|
+
active: raw.active,
|
|
704
|
+
promptSetId: raw.prompt_set_id
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
var SdkPromptSetService = class {
|
|
708
|
+
client;
|
|
709
|
+
constructor(opts) {
|
|
710
|
+
this.client = new RedTeamClient(opts);
|
|
711
|
+
}
|
|
712
|
+
async createPromptSet(name, description) {
|
|
713
|
+
const response = await this.client.customAttacks.createPromptSet({
|
|
714
|
+
name,
|
|
715
|
+
...description ? { description } : {}
|
|
716
|
+
});
|
|
717
|
+
return { uuid: response.uuid, name: response.name };
|
|
718
|
+
}
|
|
719
|
+
async addPrompt(promptSetId, prompt, goal) {
|
|
720
|
+
const response = await this.client.customAttacks.createPrompt({
|
|
721
|
+
prompt,
|
|
722
|
+
prompt_set_id: promptSetId,
|
|
723
|
+
...goal ? { goal } : {}
|
|
724
|
+
});
|
|
725
|
+
return { uuid: response.uuid, prompt: response.prompt };
|
|
726
|
+
}
|
|
727
|
+
async listPromptSets() {
|
|
728
|
+
const response = await this.client.customAttacks.listPromptSets();
|
|
729
|
+
return (response.data ?? []).map((ps) => ({
|
|
730
|
+
uuid: ps.uuid,
|
|
731
|
+
name: ps.name,
|
|
732
|
+
active: ps.active
|
|
733
|
+
}));
|
|
734
|
+
}
|
|
735
|
+
async getPromptSet(uuid) {
|
|
736
|
+
const response = await this.client.customAttacks.getPromptSet(uuid);
|
|
737
|
+
return normalizePromptSet(response);
|
|
738
|
+
}
|
|
739
|
+
async updatePromptSet(uuid, request) {
|
|
740
|
+
const response = await this.client.customAttacks.updatePromptSet(uuid, request);
|
|
741
|
+
return normalizePromptSet(response);
|
|
742
|
+
}
|
|
743
|
+
async archivePromptSet(uuid, archive) {
|
|
744
|
+
await this.client.customAttacks.archivePromptSet(uuid, { archive });
|
|
745
|
+
}
|
|
746
|
+
async getPromptSetVersionInfo(uuid) {
|
|
747
|
+
const response = await this.client.customAttacks.getPromptSetVersionInfo(uuid);
|
|
748
|
+
const raw = response;
|
|
749
|
+
return {
|
|
750
|
+
uuid: raw.uuid,
|
|
751
|
+
version: raw.version,
|
|
752
|
+
stats: raw.stats
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Fetch a prompt set and its version info in one call, degrading gracefully when the
|
|
757
|
+
* version-info endpoint is unavailable. The upstream `/version-info` route currently
|
|
758
|
+
* returns 500 for every prompt set (see prisma-airs-cli#117); without this, a 500 there
|
|
759
|
+
* would abort the whole `prompt-sets get` command even though the set itself fetched fine.
|
|
760
|
+
* The set is always returned; `versionInfo` is `undefined` when that lookup fails.
|
|
761
|
+
*/
|
|
762
|
+
async getPromptSetWithVersionInfo(uuid) {
|
|
763
|
+
const set = await this.getPromptSet(uuid);
|
|
764
|
+
let versionInfo;
|
|
765
|
+
try {
|
|
766
|
+
versionInfo = await this.getPromptSetVersionInfo(uuid);
|
|
767
|
+
} catch {
|
|
768
|
+
versionInfo = void 0;
|
|
769
|
+
}
|
|
770
|
+
return { set, versionInfo };
|
|
771
|
+
}
|
|
772
|
+
async downloadTemplate(uuid) {
|
|
773
|
+
return this.client.customAttacks.downloadTemplate(uuid);
|
|
774
|
+
}
|
|
775
|
+
async uploadPromptsCsv(uuid, file) {
|
|
776
|
+
const response = await this.client.customAttacks.uploadPromptsCsv(uuid, file);
|
|
777
|
+
return response;
|
|
778
|
+
}
|
|
779
|
+
async listPrompts(setUuid, opts) {
|
|
780
|
+
const response = await this.client.customAttacks.listPrompts(setUuid, opts);
|
|
781
|
+
return response.data.map(normalizePrompt);
|
|
782
|
+
}
|
|
783
|
+
async getPrompt(setUuid, promptUuid) {
|
|
784
|
+
const response = await this.client.customAttacks.getPrompt(setUuid, promptUuid);
|
|
785
|
+
return normalizePrompt(response);
|
|
786
|
+
}
|
|
787
|
+
async updatePrompt(setUuid, promptUuid, request) {
|
|
788
|
+
const response = await this.client.customAttacks.updatePrompt(
|
|
789
|
+
setUuid,
|
|
790
|
+
promptUuid,
|
|
791
|
+
request
|
|
792
|
+
);
|
|
793
|
+
return normalizePrompt(response);
|
|
794
|
+
}
|
|
795
|
+
async deletePrompt(setUuid, promptUuid) {
|
|
796
|
+
await this.client.customAttacks.deletePrompt(setUuid, promptUuid);
|
|
797
|
+
}
|
|
798
|
+
async getPropertyNames() {
|
|
799
|
+
const response = await this.client.customAttacks.getPropertyNames();
|
|
800
|
+
const raw = response;
|
|
801
|
+
return raw.data ?? [];
|
|
802
|
+
}
|
|
803
|
+
async createPropertyName(name) {
|
|
804
|
+
const response = await this.client.customAttacks.createPropertyName({ name });
|
|
805
|
+
return response;
|
|
806
|
+
}
|
|
807
|
+
async getPropertyValues(name) {
|
|
808
|
+
const response = await this.client.customAttacks.getPropertyValues(name);
|
|
809
|
+
const raw = response;
|
|
810
|
+
return { name: raw.name ?? name, values: raw.values ?? [] };
|
|
811
|
+
}
|
|
812
|
+
async createPropertyValue(name, value) {
|
|
813
|
+
const response = await this.client.customAttacks.createPropertyValue({
|
|
814
|
+
property_name: name,
|
|
815
|
+
property_value: value
|
|
816
|
+
});
|
|
817
|
+
return response;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/airs/redteam.ts
|
|
822
|
+
import { RedTeamClient as RedTeamClient2 } from "@cdot65/prisma-airs-sdk";
|
|
823
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["COMPLETED", "PARTIALLY_COMPLETE", "FAILED", "ABORTED"]);
|
|
824
|
+
var DEFAULT_DYNAMIC_BREADTH = 6;
|
|
825
|
+
var DEFAULT_DYNAMIC_DEPTH = 10;
|
|
826
|
+
function normalizeJob(raw) {
|
|
827
|
+
const target = raw.target;
|
|
828
|
+
return {
|
|
829
|
+
uuid: raw.uuid,
|
|
830
|
+
name: raw.name,
|
|
831
|
+
status: raw.status,
|
|
832
|
+
jobType: raw.job_type,
|
|
833
|
+
targetId: raw.target_id,
|
|
834
|
+
targetName: target?.name,
|
|
835
|
+
score: raw.score,
|
|
836
|
+
asr: raw.asr,
|
|
837
|
+
total: raw.total,
|
|
838
|
+
completed: raw.completed,
|
|
839
|
+
createdAt: raw.created_at
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
var REPORT_SUMMARY_TOKENS = {
|
|
843
|
+
"{{CRITICAL_RISK}}": "critical risk",
|
|
844
|
+
"{{HIGH_RISK}}": "high risk",
|
|
845
|
+
"{{MEDIUM_RISK}}": "medium risk",
|
|
846
|
+
"{{LOW_RISK}}": "low risk",
|
|
847
|
+
"{{INFORMATIONAL_RISK}}": "informational risk"
|
|
848
|
+
};
|
|
849
|
+
function interpolateReportSummary(summary) {
|
|
850
|
+
if (summary == null || summary === "") return summary;
|
|
851
|
+
let out = summary;
|
|
852
|
+
for (const [token, replacement] of Object.entries(REPORT_SUMMARY_TOKENS)) {
|
|
853
|
+
if (out.includes(token)) out = out.split(token).join(replacement);
|
|
854
|
+
}
|
|
855
|
+
return out;
|
|
856
|
+
}
|
|
857
|
+
function sanitizeTargetMetadata(metadata) {
|
|
858
|
+
if (!metadata) return metadata;
|
|
859
|
+
if (metadata.multi_turn === false && "multi_turn_error_message" in metadata) {
|
|
860
|
+
const { multi_turn_error_message: _drop, ...rest } = metadata;
|
|
861
|
+
return rest;
|
|
862
|
+
}
|
|
863
|
+
return metadata;
|
|
864
|
+
}
|
|
865
|
+
function normalizeTargetDetail(raw) {
|
|
866
|
+
return {
|
|
867
|
+
uuid: raw.uuid,
|
|
868
|
+
name: raw.name,
|
|
869
|
+
status: raw.status,
|
|
870
|
+
targetType: raw.target_type,
|
|
871
|
+
active: raw.active,
|
|
872
|
+
connectionType: raw.connection_type,
|
|
873
|
+
apiEndpointType: raw.api_endpoint_type,
|
|
874
|
+
responseMode: raw.response_mode,
|
|
875
|
+
authType: raw.auth_type,
|
|
876
|
+
authConfig: raw.auth_config,
|
|
877
|
+
networkBrokerChannelUuid: raw.network_broker_channel_uuid,
|
|
878
|
+
sessionSupported: raw.session_supported,
|
|
879
|
+
extraInfo: raw.extra_info,
|
|
880
|
+
description: raw.description,
|
|
881
|
+
connectionParams: raw.connection_params,
|
|
882
|
+
background: raw.target_background,
|
|
883
|
+
additionalContext: raw.additional_context,
|
|
884
|
+
metadata: sanitizeTargetMetadata(raw.target_metadata)
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
var SdkRedTeamService = class {
|
|
888
|
+
client;
|
|
889
|
+
constructor(opts) {
|
|
890
|
+
this.client = new RedTeamClient2(opts);
|
|
891
|
+
}
|
|
892
|
+
async getEulaContent() {
|
|
893
|
+
const response = await this.client.eula.getContent();
|
|
894
|
+
return { content: response.content };
|
|
895
|
+
}
|
|
896
|
+
async getEulaStatus() {
|
|
897
|
+
const raw = await this.client.eula.getStatus();
|
|
898
|
+
return {
|
|
899
|
+
isAccepted: raw.is_accepted,
|
|
900
|
+
acceptedAt: raw.accepted_at,
|
|
901
|
+
acceptedByUserId: raw.accepted_by_user_id
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
async acceptEula(eulaContent) {
|
|
905
|
+
const raw = await this.client.eula.accept({
|
|
906
|
+
eula_content: eulaContent,
|
|
907
|
+
accepted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
908
|
+
});
|
|
909
|
+
return {
|
|
910
|
+
isAccepted: raw.is_accepted,
|
|
911
|
+
acceptedAt: raw.accepted_at,
|
|
912
|
+
acceptedByUserId: raw.accepted_by_user_id
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
async createInstance(request) {
|
|
916
|
+
const raw = await this.client.instances.createInstance({
|
|
917
|
+
tsg_id: request.tsgId,
|
|
918
|
+
tenant_id: request.tenantId,
|
|
919
|
+
app_id: request.appId,
|
|
920
|
+
region: request.region
|
|
921
|
+
});
|
|
922
|
+
return {
|
|
923
|
+
tsgId: raw.tsg_id,
|
|
924
|
+
tenantId: raw.tenant_id,
|
|
925
|
+
appId: raw.app_id,
|
|
926
|
+
isSuccess: raw.is_success
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
async getInstance(tenantId) {
|
|
930
|
+
const raw = await this.client.instances.getInstance(tenantId);
|
|
931
|
+
return {
|
|
932
|
+
tsgId: raw.tsg_id,
|
|
933
|
+
tenantId: raw.tenant_id,
|
|
934
|
+
appId: raw.app_id,
|
|
935
|
+
region: raw.region
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
async updateInstance(tenantId, request) {
|
|
939
|
+
const raw = await this.client.instances.updateInstance(tenantId, {
|
|
940
|
+
tsg_id: request.tsgId,
|
|
941
|
+
tenant_id: request.tenantId,
|
|
942
|
+
app_id: request.appId,
|
|
943
|
+
region: request.region
|
|
944
|
+
});
|
|
945
|
+
return {
|
|
946
|
+
tsgId: raw.tsg_id,
|
|
947
|
+
tenantId: raw.tenant_id,
|
|
948
|
+
appId: raw.app_id,
|
|
949
|
+
isSuccess: raw.is_success
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
async deleteInstance(tenantId) {
|
|
953
|
+
const raw = await this.client.instances.deleteInstance(tenantId);
|
|
954
|
+
return {
|
|
955
|
+
tsgId: raw.tsg_id,
|
|
956
|
+
tenantId: raw.tenant_id,
|
|
957
|
+
appId: raw.app_id,
|
|
958
|
+
isSuccess: raw.is_success
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
async createDevices(tenantId, request) {
|
|
962
|
+
return await this.client.instances.createDevices(tenantId, request);
|
|
963
|
+
}
|
|
964
|
+
async updateDevices(tenantId, request) {
|
|
965
|
+
return await this.client.instances.updateDevices(tenantId, request);
|
|
966
|
+
}
|
|
967
|
+
async deleteDevices(tenantId, serialNumbers) {
|
|
968
|
+
return await this.client.instances.deleteDevices(tenantId, serialNumbers);
|
|
969
|
+
}
|
|
970
|
+
async getRegistryCredentials() {
|
|
971
|
+
const raw = await this.client.instances.getRegistryCredentials();
|
|
972
|
+
return {
|
|
973
|
+
token: raw.token,
|
|
974
|
+
expiry: raw.expiry
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async validateTargetAuth(request) {
|
|
978
|
+
const sdkRequest = {
|
|
979
|
+
auth_type: request.authType,
|
|
980
|
+
auth_config: request.authConfig
|
|
981
|
+
};
|
|
982
|
+
if (request.targetId) sdkRequest.target_id = request.targetId;
|
|
983
|
+
const raw = await this.client.targets.validateAuth(sdkRequest);
|
|
984
|
+
return {
|
|
985
|
+
validated: raw.validated,
|
|
986
|
+
tokenPreview: raw.token_preview,
|
|
987
|
+
expiresIn: raw.expires_in
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
async getTargetMetadata() {
|
|
991
|
+
return await this.client.targets.getTargetMetadata();
|
|
992
|
+
}
|
|
993
|
+
async getTargetTemplates() {
|
|
994
|
+
return await this.client.targets.getTargetTemplates();
|
|
995
|
+
}
|
|
996
|
+
async listTargets() {
|
|
997
|
+
const all = [];
|
|
998
|
+
let skip = 0;
|
|
999
|
+
const limit = 100;
|
|
1000
|
+
for (; ; ) {
|
|
1001
|
+
const response = this.client.targets.list({ skip, limit });
|
|
1002
|
+
const body = await response;
|
|
1003
|
+
const data = body.data;
|
|
1004
|
+
for (const t of data) {
|
|
1005
|
+
all.push({
|
|
1006
|
+
uuid: t.uuid,
|
|
1007
|
+
name: t.name,
|
|
1008
|
+
status: t.status,
|
|
1009
|
+
targetType: t.target_type,
|
|
1010
|
+
active: t.active
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
const pagination = body.pagination;
|
|
1014
|
+
if (!pagination || all.length >= (pagination.total ?? data.length) || data.length < limit) {
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
skip += limit;
|
|
1018
|
+
}
|
|
1019
|
+
return all;
|
|
1020
|
+
}
|
|
1021
|
+
async getTarget(uuid) {
|
|
1022
|
+
const response = await this.client.targets.get(uuid);
|
|
1023
|
+
return normalizeTargetDetail(response);
|
|
1024
|
+
}
|
|
1025
|
+
async createTarget(request, opts) {
|
|
1026
|
+
const response = await this.client.targets.create(request, opts);
|
|
1027
|
+
return normalizeTargetDetail(response);
|
|
1028
|
+
}
|
|
1029
|
+
async updateTarget(uuid, request, opts) {
|
|
1030
|
+
const response = await this.client.targets.update(uuid, request, opts);
|
|
1031
|
+
return normalizeTargetDetail(response);
|
|
1032
|
+
}
|
|
1033
|
+
async deleteTarget(uuid) {
|
|
1034
|
+
await this.client.targets.delete(uuid);
|
|
1035
|
+
}
|
|
1036
|
+
async probeTarget(request) {
|
|
1037
|
+
const response = await this.client.targets.probe(request);
|
|
1038
|
+
const raw = response;
|
|
1039
|
+
const meta = raw.target_metadata;
|
|
1040
|
+
if (meta && typeof meta === "object") {
|
|
1041
|
+
raw.target_metadata = sanitizeTargetMetadata(meta);
|
|
1042
|
+
}
|
|
1043
|
+
return raw;
|
|
1044
|
+
}
|
|
1045
|
+
async getTargetProfile(uuid) {
|
|
1046
|
+
const response = await this.client.targets.getProfile(uuid);
|
|
1047
|
+
return response;
|
|
1048
|
+
}
|
|
1049
|
+
async updateTargetProfile(uuid, request) {
|
|
1050
|
+
const response = await this.client.targets.updateProfile(uuid, request);
|
|
1051
|
+
return response;
|
|
1052
|
+
}
|
|
1053
|
+
async createScan(request) {
|
|
1054
|
+
let jobMetadata = {};
|
|
1055
|
+
if (request.jobType === "STATIC" && request.categories) {
|
|
1056
|
+
jobMetadata = { categories: request.categories };
|
|
1057
|
+
} else if (request.jobType === "CUSTOM" && request.customPromptSets) {
|
|
1058
|
+
jobMetadata = {
|
|
1059
|
+
custom_prompt_sets: request.customPromptSets
|
|
1060
|
+
};
|
|
1061
|
+
} else if (request.jobType === "DYNAMIC") {
|
|
1062
|
+
jobMetadata = {
|
|
1063
|
+
stream_breadth: request.streamBreadth ?? DEFAULT_DYNAMIC_BREADTH,
|
|
1064
|
+
stream_depth: request.streamDepth ?? DEFAULT_DYNAMIC_DEPTH,
|
|
1065
|
+
...request.attackGoals?.length ? { attack_goals: request.attackGoals } : {}
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
const response = await this.client.scans.create({
|
|
1069
|
+
name: request.name,
|
|
1070
|
+
target: { uuid: request.targetUuid },
|
|
1071
|
+
job_type: request.jobType,
|
|
1072
|
+
job_metadata: jobMetadata
|
|
1073
|
+
});
|
|
1074
|
+
return normalizeJob(response);
|
|
1075
|
+
}
|
|
1076
|
+
async getScan(jobId) {
|
|
1077
|
+
const response = await this.client.scans.get(jobId);
|
|
1078
|
+
return normalizeJob(response);
|
|
1079
|
+
}
|
|
1080
|
+
async listScans(opts) {
|
|
1081
|
+
const sdkOpts = {};
|
|
1082
|
+
if (opts?.status) sdkOpts.status = opts.status;
|
|
1083
|
+
if (opts?.jobType) sdkOpts.job_type = opts.jobType;
|
|
1084
|
+
if (opts?.targetId) sdkOpts.target_id = opts.targetId;
|
|
1085
|
+
if (opts?.limit) sdkOpts.limit = opts.limit;
|
|
1086
|
+
const response = await this.client.scans.list(sdkOpts);
|
|
1087
|
+
return response.data.map(
|
|
1088
|
+
normalizeJob
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
async abortScan(jobId) {
|
|
1092
|
+
await this.client.scans.abort(jobId);
|
|
1093
|
+
}
|
|
1094
|
+
async getStaticReport(jobId) {
|
|
1095
|
+
const raw = await this.client.reports.getStaticReport(jobId);
|
|
1096
|
+
const severityReport = raw.severity_report;
|
|
1097
|
+
const stats = severityReport?.stats ?? [];
|
|
1098
|
+
const securityReport = raw.security_report;
|
|
1099
|
+
const subCategories = securityReport?.sub_categories ?? [];
|
|
1100
|
+
return {
|
|
1101
|
+
score: raw.score,
|
|
1102
|
+
asr: raw.asr,
|
|
1103
|
+
severityBreakdown: stats.map((s) => ({
|
|
1104
|
+
severity: s.severity,
|
|
1105
|
+
successful: s.successful ?? 0,
|
|
1106
|
+
failed: s.failed ?? 0
|
|
1107
|
+
})),
|
|
1108
|
+
reportSummary: interpolateReportSummary(raw.report_summary),
|
|
1109
|
+
categories: subCategories.map((sc) => {
|
|
1110
|
+
const successful = sc.successful ?? 0;
|
|
1111
|
+
const failed = sc.failed ?? 0;
|
|
1112
|
+
const total = sc.total ?? successful + failed;
|
|
1113
|
+
return {
|
|
1114
|
+
id: sc.id,
|
|
1115
|
+
displayName: sc.display_name,
|
|
1116
|
+
asr: total > 0 ? successful / total * 100 : 0,
|
|
1117
|
+
successful,
|
|
1118
|
+
failed,
|
|
1119
|
+
total
|
|
1120
|
+
};
|
|
1121
|
+
})
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
async getDynamicReport(jobId) {
|
|
1125
|
+
const raw = await this.client.reports.getDynamicReport(jobId);
|
|
1126
|
+
return {
|
|
1127
|
+
score: raw.score,
|
|
1128
|
+
asr: raw.asr,
|
|
1129
|
+
totalGoals: raw.total_goals,
|
|
1130
|
+
goalsAchieved: raw.goals_achieved,
|
|
1131
|
+
totalStreams: raw.total_streams,
|
|
1132
|
+
totalThreats: raw.total_threats,
|
|
1133
|
+
reportSummary: interpolateReportSummary(raw.report_summary)
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
async getCustomReport(jobId) {
|
|
1137
|
+
const raw = await this.client.customAttackReports.getReport(jobId);
|
|
1138
|
+
const reports = raw.custom_attack_reports ?? [];
|
|
1139
|
+
return {
|
|
1140
|
+
totalPrompts: raw.total_prompts,
|
|
1141
|
+
totalAttacks: raw.total_attacks,
|
|
1142
|
+
totalThreats: raw.total_threats,
|
|
1143
|
+
failedAttacks: raw.failed_attacks,
|
|
1144
|
+
score: raw.score,
|
|
1145
|
+
asr: raw.asr,
|
|
1146
|
+
promptSets: reports.map((r) => ({
|
|
1147
|
+
promptSetId: r.prompt_set_id,
|
|
1148
|
+
promptSetName: r.prompt_set_name,
|
|
1149
|
+
totalPrompts: r.total_prompts,
|
|
1150
|
+
totalAttacks: r.total_attacks,
|
|
1151
|
+
totalThreats: r.total_threats,
|
|
1152
|
+
threatRate: r.threat_rate
|
|
1153
|
+
}))
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
async listAttacks(jobId, opts) {
|
|
1157
|
+
const response = await this.client.reports.listAttacks(jobId, opts);
|
|
1158
|
+
const data = response.data ?? [];
|
|
1159
|
+
const pagination = response.pagination;
|
|
1160
|
+
return {
|
|
1161
|
+
attacks: data.map((a) => ({
|
|
1162
|
+
id: a.uuid,
|
|
1163
|
+
name: a.attack_name,
|
|
1164
|
+
severity: a.severity,
|
|
1165
|
+
category: a.category,
|
|
1166
|
+
subCategory: a.sub_category,
|
|
1167
|
+
subCategoryDisplayName: a.sub_category_display_name,
|
|
1168
|
+
successful: a.threat ?? false
|
|
1169
|
+
})),
|
|
1170
|
+
totalItems: pagination?.total_items
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
async listCustomAttacks(jobId, opts) {
|
|
1174
|
+
const response = await this.client.customAttackReports.listCustomAttacks(jobId, opts);
|
|
1175
|
+
return response.data.map(
|
|
1176
|
+
(a) => ({
|
|
1177
|
+
promptId: a.prompt_id,
|
|
1178
|
+
promptText: a.prompt_text,
|
|
1179
|
+
goal: a.goal,
|
|
1180
|
+
threat: a.threat ?? false,
|
|
1181
|
+
asr: a.asr,
|
|
1182
|
+
promptSetName: a.prompt_set_name
|
|
1183
|
+
})
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
async getCategories() {
|
|
1187
|
+
const response = await this.client.scans.getCategories();
|
|
1188
|
+
return response.map((c) => ({
|
|
1189
|
+
id: c.id,
|
|
1190
|
+
displayName: c.display_name,
|
|
1191
|
+
description: c.description,
|
|
1192
|
+
subCategories: (c.sub_categories ?? []).map((sc) => ({
|
|
1193
|
+
id: sc.id,
|
|
1194
|
+
displayName: sc.display_name,
|
|
1195
|
+
description: sc.description
|
|
1196
|
+
}))
|
|
1197
|
+
}));
|
|
1198
|
+
}
|
|
1199
|
+
async waitForCompletion(jobId, onProgress, intervalMs = 5e3) {
|
|
1200
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1201
|
+
while (true) {
|
|
1202
|
+
const job = await this.getScan(jobId);
|
|
1203
|
+
onProgress?.(job);
|
|
1204
|
+
if (job.status === "FAILED") {
|
|
1205
|
+
throw new Error(`Scan ${jobId} failed`);
|
|
1206
|
+
}
|
|
1207
|
+
if (TERMINAL_STATUSES.has(job.status)) {
|
|
1208
|
+
return job;
|
|
1209
|
+
}
|
|
1210
|
+
await delay(intervalMs);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
// src/airs/runtime.ts
|
|
1216
|
+
import { Content, init, Scanner } from "@cdot65/prisma-airs-sdk";
|
|
1217
|
+
var BATCH_SIZE = 5;
|
|
1218
|
+
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
1219
|
+
var DEFAULT_MAX_RETRIES = 5;
|
|
1220
|
+
var DEFAULT_BASE_DELAY_MS = 1e4;
|
|
1221
|
+
function isRateLimitError(err) {
|
|
1222
|
+
if (err instanceof Error) {
|
|
1223
|
+
const msg = err.message.toLowerCase();
|
|
1224
|
+
return msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429");
|
|
1225
|
+
}
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
var SdkRuntimeService = class {
|
|
1229
|
+
scanner;
|
|
1230
|
+
constructor(opts) {
|
|
1231
|
+
init(opts);
|
|
1232
|
+
this.scanner = new Scanner();
|
|
1233
|
+
}
|
|
1234
|
+
async scanPrompt(profileName, prompt, response) {
|
|
1235
|
+
const contentOpts = { prompt };
|
|
1236
|
+
if (response) contentOpts.response = response;
|
|
1237
|
+
const content = new Content(contentOpts);
|
|
1238
|
+
const res = await this.scanner.syncScan({ profile_name: profileName }, content, void 0);
|
|
1239
|
+
const detected = res.prompt_detected ?? {};
|
|
1240
|
+
const triggered = !!(detected.topic_violation || detected.injection || detected.toxic_content || detected.dlp || detected.url_cats || detected.malicious_code);
|
|
1241
|
+
return {
|
|
1242
|
+
prompt,
|
|
1243
|
+
response,
|
|
1244
|
+
scanId: res.scan_id ?? "",
|
|
1245
|
+
reportId: res.report_id ?? "",
|
|
1246
|
+
action: res.action === "block" ? "block" : "allow",
|
|
1247
|
+
category: res.category ?? "unknown",
|
|
1248
|
+
triggered,
|
|
1249
|
+
detections: detected
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
async submitBulkScan(profileName, prompts, sessionId) {
|
|
1253
|
+
const scanIds = [];
|
|
1254
|
+
for (let i = 0; i < prompts.length; i += BATCH_SIZE) {
|
|
1255
|
+
const batch = prompts.slice(i, i + BATCH_SIZE);
|
|
1256
|
+
const scanObjects = batch.map((prompt, idx) => ({
|
|
1257
|
+
req_id: i + idx,
|
|
1258
|
+
scan_req: {
|
|
1259
|
+
ai_profile: { profile_name: profileName },
|
|
1260
|
+
contents: [{ prompt }],
|
|
1261
|
+
...sessionId ? { session_id: sessionId } : {}
|
|
1262
|
+
}
|
|
1263
|
+
}));
|
|
1264
|
+
const res = await this.scanner.asyncScan(scanObjects);
|
|
1265
|
+
scanIds.push(res.scan_id);
|
|
1266
|
+
}
|
|
1267
|
+
return scanIds;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Poll async scan results until all complete or fail.
|
|
1271
|
+
*
|
|
1272
|
+
* Note: The async query API (`queryByScanIds`) does not return `prompt`,
|
|
1273
|
+
* `response`, `triggered`, or `detections` fields. These are set to
|
|
1274
|
+
* defaults (`''`, `undefined`, `false`, `{}`) in the returned results.
|
|
1275
|
+
* Use `scanPrompt()` (sync API) when these fields are needed.
|
|
1276
|
+
*/
|
|
1277
|
+
async pollResults(scanIds, intervalMs = DEFAULT_POLL_INTERVAL_MS, retryOpts) {
|
|
1278
|
+
const maxRetries = retryOpts?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1279
|
+
const baseDelay = retryOpts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
1280
|
+
const completed = /* @__PURE__ */ new Map();
|
|
1281
|
+
const pending = new Set(scanIds);
|
|
1282
|
+
let retryLevel = 0;
|
|
1283
|
+
while (pending.size > 0) {
|
|
1284
|
+
const pendingIds = [...pending];
|
|
1285
|
+
let sweepCompleted = true;
|
|
1286
|
+
for (let b = 0; b < pendingIds.length; b += 5) {
|
|
1287
|
+
const batch = pendingIds.slice(b, b + 5);
|
|
1288
|
+
let results;
|
|
1289
|
+
try {
|
|
1290
|
+
results = await this.scanner.queryByScanIds(batch);
|
|
1291
|
+
} catch (err) {
|
|
1292
|
+
if (isRateLimitError(err) && retryLevel < maxRetries) {
|
|
1293
|
+
retryLevel++;
|
|
1294
|
+
const delayMs = baseDelay * 2 ** (retryLevel - 1);
|
|
1295
|
+
retryOpts?.onRetry?.(retryLevel, delayMs);
|
|
1296
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1297
|
+
sweepCompleted = false;
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
throw err;
|
|
1301
|
+
}
|
|
1302
|
+
this.processQueryResults(results, completed, pending);
|
|
1303
|
+
if (b + 5 < pendingIds.length) {
|
|
1304
|
+
const batchDelay = retryLevel > 0 ? baseDelay : Math.min(baseDelay, 1e3);
|
|
1305
|
+
await new Promise((resolve) => setTimeout(resolve, batchDelay));
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
if (sweepCompleted && retryLevel > 0) {
|
|
1309
|
+
retryLevel = Math.max(0, retryLevel - 1);
|
|
1310
|
+
}
|
|
1311
|
+
if (pending.size > 0) {
|
|
1312
|
+
const sweepDelay = retryLevel > 0 ? baseDelay * 2 ** retryLevel : intervalMs;
|
|
1313
|
+
await new Promise((resolve) => setTimeout(resolve, sweepDelay));
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return scanIds.map((id) => completed.get(id));
|
|
1317
|
+
}
|
|
1318
|
+
processQueryResults(results, completed, pending) {
|
|
1319
|
+
for (const r of results) {
|
|
1320
|
+
const id = r.scan_id ?? "";
|
|
1321
|
+
const status = (r.status ?? "").toLowerCase();
|
|
1322
|
+
if ((status === "complete" || status === "completed") && r.result) {
|
|
1323
|
+
const result = r.result;
|
|
1324
|
+
completed.set(id, {
|
|
1325
|
+
prompt: "",
|
|
1326
|
+
// not available from async API
|
|
1327
|
+
response: void 0,
|
|
1328
|
+
// not available from async API
|
|
1329
|
+
scanId: result.scan_id ?? id,
|
|
1330
|
+
reportId: result.report_id ?? "",
|
|
1331
|
+
action: result.action === "block" ? "block" : "allow",
|
|
1332
|
+
category: result.category ?? "unknown",
|
|
1333
|
+
triggered: false,
|
|
1334
|
+
// not available from async API — always false
|
|
1335
|
+
detections: {}
|
|
1336
|
+
// not available from async API
|
|
1337
|
+
});
|
|
1338
|
+
pending.delete(id);
|
|
1339
|
+
} else if (status === "failed") {
|
|
1340
|
+
completed.set(id, {
|
|
1341
|
+
prompt: "",
|
|
1342
|
+
// not available from async API
|
|
1343
|
+
response: void 0,
|
|
1344
|
+
// not available from async API
|
|
1345
|
+
scanId: id,
|
|
1346
|
+
reportId: "",
|
|
1347
|
+
action: "allow",
|
|
1348
|
+
// safe default for failed scans
|
|
1349
|
+
category: "error",
|
|
1350
|
+
triggered: false,
|
|
1351
|
+
// not available from async API — always false
|
|
1352
|
+
detections: {}
|
|
1353
|
+
// not available from async API
|
|
1354
|
+
});
|
|
1355
|
+
pending.delete(id);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
static formatResultsCsv(results) {
|
|
1360
|
+
const header = "prompt,action,category,triggered,scan_id,report_id";
|
|
1361
|
+
const rows = results.map((r) => {
|
|
1362
|
+
const escaped = r.prompt.replace(/"/g, '""');
|
|
1363
|
+
return `"${escaped}","${r.action}","${r.category}","${r.triggered}","${r.scanId}","${r.reportId}"`;
|
|
1364
|
+
});
|
|
1365
|
+
return [header, ...rows].join("\n");
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
// src/airs/scanner.ts
|
|
1370
|
+
import * as fs from "fs/promises";
|
|
1371
|
+
import { Content as Content2, init as init2, Scanner as Scanner2 } from "@cdot65/prisma-airs-sdk";
|
|
1372
|
+
import pLimit from "p-limit";
|
|
1373
|
+
var AirsScanService = class {
|
|
1374
|
+
scanner;
|
|
1375
|
+
constructor(opts) {
|
|
1376
|
+
init2(opts);
|
|
1377
|
+
this.scanner = new Scanner2();
|
|
1378
|
+
}
|
|
1379
|
+
/** Scan a single prompt synchronously and return the normalized result. */
|
|
1380
|
+
async scan(profileName, prompt, sessionId) {
|
|
1381
|
+
const content = new Content2({ prompt });
|
|
1382
|
+
const response = await this.scanner.syncScan(
|
|
1383
|
+
{ profile_name: profileName },
|
|
1384
|
+
content,
|
|
1385
|
+
sessionId ? { sessionId } : void 0
|
|
1386
|
+
);
|
|
1387
|
+
const action = response.action === "block" ? "block" : "allow";
|
|
1388
|
+
const detected = response.prompt_detected;
|
|
1389
|
+
const triggered = detected?.topic_violation === true;
|
|
1390
|
+
const category = response.category ?? void 0;
|
|
1391
|
+
return {
|
|
1392
|
+
scanId: response.scan_id ?? "",
|
|
1393
|
+
reportId: response.report_id ?? "",
|
|
1394
|
+
action,
|
|
1395
|
+
triggered,
|
|
1396
|
+
category,
|
|
1397
|
+
raw: response
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
/** Scan multiple prompts concurrently (default 5) and return results in order. */
|
|
1401
|
+
async scanBatch(profileName, prompts, concurrency = 5, sessionId) {
|
|
1402
|
+
const limit = pLimit(concurrency);
|
|
1403
|
+
return Promise.all(
|
|
1404
|
+
prompts.map((prompt) => limit(() => this.scan(profileName, prompt, sessionId)))
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
var RateLimitedScanService = class {
|
|
1409
|
+
constructor(inner, maxPerSecond) {
|
|
1410
|
+
this.inner = inner;
|
|
1411
|
+
this.maxPerSecond = maxPerSecond;
|
|
1412
|
+
}
|
|
1413
|
+
timestamps = [];
|
|
1414
|
+
async acquireToken() {
|
|
1415
|
+
const now = Date.now();
|
|
1416
|
+
this.timestamps = this.timestamps.filter((t) => now - t < 1e3);
|
|
1417
|
+
if (this.timestamps.length >= this.maxPerSecond) {
|
|
1418
|
+
const oldest = this.timestamps[0];
|
|
1419
|
+
const waitMs = 1e3 - (now - oldest);
|
|
1420
|
+
if (waitMs > 0) {
|
|
1421
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
1422
|
+
}
|
|
1423
|
+
const after = Date.now();
|
|
1424
|
+
this.timestamps = this.timestamps.filter((t) => after - t < 1e3);
|
|
1425
|
+
}
|
|
1426
|
+
this.timestamps.push(Date.now());
|
|
1427
|
+
}
|
|
1428
|
+
async scan(profileName, prompt, sessionId) {
|
|
1429
|
+
await this.acquireToken();
|
|
1430
|
+
return this.inner.scan(profileName, prompt, sessionId);
|
|
1431
|
+
}
|
|
1432
|
+
async scanBatch(profileName, prompts, concurrency, sessionId) {
|
|
1433
|
+
const limit = pLimit(concurrency ?? 5);
|
|
1434
|
+
return Promise.all(
|
|
1435
|
+
prompts.map((prompt) => limit(() => this.scan(profileName, prompt, sessionId)))
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
// src/backup/io.ts
|
|
1441
|
+
import fs2 from "fs";
|
|
1442
|
+
import path from "path";
|
|
1443
|
+
import yaml from "js-yaml";
|
|
1444
|
+
function sanitizeFilename(name) {
|
|
1445
|
+
if (!name) return "unnamed";
|
|
1446
|
+
const sanitized = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
1447
|
+
return sanitized || "unnamed";
|
|
1448
|
+
}
|
|
1449
|
+
function resolveOutputDir(userDir, defaultSubdir) {
|
|
1450
|
+
if (userDir) return path.resolve(userDir);
|
|
1451
|
+
return path.resolve("./airs-backup", defaultSubdir);
|
|
1452
|
+
}
|
|
1453
|
+
function writeBackupFile(dir, filename, envelope, format) {
|
|
1454
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1455
|
+
const ext = format === "yaml" ? "yaml" : "json";
|
|
1456
|
+
const filePath = path.join(dir, `${filename}.${ext}`);
|
|
1457
|
+
const content = format === "yaml" ? yaml.dump(envelope, { lineWidth: -1, noRefs: true }) : `${JSON.stringify(envelope, null, 2)}
|
|
1458
|
+
`;
|
|
1459
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
1460
|
+
}
|
|
1461
|
+
function readBackupFile(filePath) {
|
|
1462
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
1463
|
+
if (ext !== ".json" && ext !== ".yaml" && ext !== ".yml") {
|
|
1464
|
+
throw new Error(`Unsupported file format: ${ext} (expected .json, .yaml, or .yml)`);
|
|
1465
|
+
}
|
|
1466
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
1467
|
+
const parsed = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
|
|
1468
|
+
if (!parsed || typeof parsed !== "object" || !("version" in parsed) || !("data" in parsed)) {
|
|
1469
|
+
throw new Error(`Invalid backup file: ${filePath} (missing version or data)`);
|
|
1470
|
+
}
|
|
1471
|
+
return parsed;
|
|
1472
|
+
}
|
|
1473
|
+
function readBackupDir(dirPath, resourceType) {
|
|
1474
|
+
const files = fs2.readdirSync(dirPath).filter((f) => /\.(json|ya?ml)$/i.test(f)).map((f) => path.join(dirPath, f));
|
|
1475
|
+
const envelopes = [];
|
|
1476
|
+
for (const filePath of files) {
|
|
1477
|
+
try {
|
|
1478
|
+
const envelope = readBackupFile(filePath);
|
|
1479
|
+
if (envelope.resourceType === resourceType) {
|
|
1480
|
+
envelopes.push(envelope);
|
|
1481
|
+
}
|
|
1482
|
+
} catch {
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return envelopes;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// src/config/loader.ts
|
|
1489
|
+
import { readFile } from "fs/promises";
|
|
1490
|
+
import { homedir } from "os";
|
|
1491
|
+
import { join } from "path";
|
|
1492
|
+
|
|
1493
|
+
// src/config/schema.ts
|
|
1494
|
+
import { z } from "zod";
|
|
1495
|
+
var ConfigSchema = z.object({
|
|
1496
|
+
// AIRS Scanner (runtime scan API)
|
|
1497
|
+
airsApiKey: z.string().optional(),
|
|
1498
|
+
airsApiToken: z.string().optional(),
|
|
1499
|
+
airsApiEndpoint: z.string().optional(),
|
|
1500
|
+
airsNumRetries: z.coerce.number().int().min(0).max(5).optional(),
|
|
1501
|
+
// AIRS Management (SDK v2 — OAuth2 client credentials)
|
|
1502
|
+
mgmtClientId: z.string().optional(),
|
|
1503
|
+
mgmtClientSecret: z.string().optional(),
|
|
1504
|
+
mgmtTsgId: z.string().optional(),
|
|
1505
|
+
mgmtEndpoint: z.string().optional(),
|
|
1506
|
+
mgmtTokenEndpoint: z.string().optional(),
|
|
1507
|
+
dlpEndpoint: z.string().optional(),
|
|
1508
|
+
// Red Team (endpoints only; creds shared with mgmt*)
|
|
1509
|
+
redTeamDataEndpoint: z.string().optional(),
|
|
1510
|
+
redTeamMgmtEndpoint: z.string().optional(),
|
|
1511
|
+
redTeamTokenEndpoint: z.string().optional(),
|
|
1512
|
+
// Model Security (endpoints only; creds shared with mgmt*)
|
|
1513
|
+
modelSecDataEndpoint: z.string().optional(),
|
|
1514
|
+
modelSecMgmtEndpoint: z.string().optional(),
|
|
1515
|
+
modelSecTokenEndpoint: z.string().optional(),
|
|
1516
|
+
// Tuning
|
|
1517
|
+
scanConcurrency: z.coerce.number().int().min(1).max(20).default(5),
|
|
1518
|
+
// Persistence
|
|
1519
|
+
dataDir: z.string().default("~/.prisma-airs/runs")
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// src/config/loader.ts
|
|
1523
|
+
function expandHome(p) {
|
|
1524
|
+
return p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
|
|
1525
|
+
}
|
|
1526
|
+
function fromEnv() {
|
|
1527
|
+
const env = process.env;
|
|
1528
|
+
return {
|
|
1529
|
+
airsApiKey: env.PANW_AI_SEC_API_KEY,
|
|
1530
|
+
airsApiToken: env.PANW_AI_SEC_API_TOKEN,
|
|
1531
|
+
airsApiEndpoint: env.PANW_AI_SEC_API_ENDPOINT,
|
|
1532
|
+
airsNumRetries: env.PANW_AI_SEC_NUM_RETRIES,
|
|
1533
|
+
mgmtClientId: env.PANW_MGMT_CLIENT_ID,
|
|
1534
|
+
mgmtClientSecret: env.PANW_MGMT_CLIENT_SECRET,
|
|
1535
|
+
mgmtTsgId: env.PANW_MGMT_TSG_ID,
|
|
1536
|
+
mgmtEndpoint: env.PANW_MGMT_ENDPOINT,
|
|
1537
|
+
mgmtTokenEndpoint: env.PANW_MGMT_TOKEN_ENDPOINT,
|
|
1538
|
+
dlpEndpoint: env.PANW_DLP_ENDPOINT,
|
|
1539
|
+
redTeamDataEndpoint: env.PANW_RED_TEAM_DATA_ENDPOINT,
|
|
1540
|
+
redTeamMgmtEndpoint: env.PANW_RED_TEAM_MGMT_ENDPOINT,
|
|
1541
|
+
redTeamTokenEndpoint: env.PANW_RED_TEAM_TOKEN_ENDPOINT,
|
|
1542
|
+
modelSecDataEndpoint: env.PANW_MODEL_SEC_DATA_ENDPOINT,
|
|
1543
|
+
modelSecMgmtEndpoint: env.PANW_MODEL_SEC_MGMT_ENDPOINT,
|
|
1544
|
+
modelSecTokenEndpoint: env.PANW_MODEL_SEC_TOKEN_ENDPOINT,
|
|
1545
|
+
scanConcurrency: env.SCAN_CONCURRENCY,
|
|
1546
|
+
dataDir: env.DATA_DIR
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
async function fromFile(path2) {
|
|
1550
|
+
try {
|
|
1551
|
+
const data = await readFile(path2, "utf-8");
|
|
1552
|
+
return JSON.parse(data);
|
|
1553
|
+
} catch {
|
|
1554
|
+
return {};
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
function stripUndefined(obj) {
|
|
1558
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0 && v !== ""));
|
|
1559
|
+
}
|
|
1560
|
+
function resolveConfigFilePath(configFilePath) {
|
|
1561
|
+
if (configFilePath) return configFilePath;
|
|
1562
|
+
const envPath = process.env.PRISMA_AIRS_CONFIG_PATH;
|
|
1563
|
+
if (envPath) return expandHome(envPath);
|
|
1564
|
+
return join(homedir(), ".prisma-airs", "config.json");
|
|
1565
|
+
}
|
|
1566
|
+
async function loadConfig(cliOverrides = {}, configFilePath) {
|
|
1567
|
+
const filePath = resolveConfigFilePath(configFilePath);
|
|
1568
|
+
const fileConfig = await fromFile(filePath);
|
|
1569
|
+
const envConfig = fromEnv();
|
|
1570
|
+
const merged = {
|
|
1571
|
+
...stripUndefined(fileConfig),
|
|
1572
|
+
...stripUndefined(envConfig),
|
|
1573
|
+
...stripUndefined(cliOverrides)
|
|
1574
|
+
};
|
|
1575
|
+
const config = ConfigSchema.parse(merged);
|
|
1576
|
+
return {
|
|
1577
|
+
...config,
|
|
1578
|
+
dataDir: expandHome(config.dataDir)
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
async function inspectConfig(configFilePath) {
|
|
1582
|
+
const filePath = resolveConfigFilePath(configFilePath);
|
|
1583
|
+
const fileConfig = stripUndefined(await fromFile(filePath));
|
|
1584
|
+
const envConfig = stripUndefined(fromEnv());
|
|
1585
|
+
const merged = { ...fileConfig, ...envConfig };
|
|
1586
|
+
const config = ConfigSchema.parse(merged);
|
|
1587
|
+
const entries = {};
|
|
1588
|
+
for (const key of Object.keys(ConfigSchema.shape)) {
|
|
1589
|
+
const source = key in envConfig ? "env" : key in fileConfig ? "file" : "default";
|
|
1590
|
+
entries[key] = { value: config[key], source };
|
|
1591
|
+
}
|
|
1592
|
+
return entries;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// src/core/constraints.ts
|
|
1596
|
+
var MAX_NAME_LENGTH = 100;
|
|
1597
|
+
var MAX_DESCRIPTION_LENGTH = 250;
|
|
1598
|
+
var MAX_EXAMPLE_LENGTH = 250;
|
|
1599
|
+
var MAX_EXAMPLES = 5;
|
|
1600
|
+
var MIN_EXAMPLES = 2;
|
|
1601
|
+
var MAX_COMBINED_LENGTH = 1e3;
|
|
1602
|
+
function byteLen(s) {
|
|
1603
|
+
return Buffer.byteLength(s, "utf8");
|
|
1604
|
+
}
|
|
1605
|
+
function validateName(name) {
|
|
1606
|
+
const errors = [];
|
|
1607
|
+
if (!name || name.length === 0) {
|
|
1608
|
+
errors.push({ field: "name", message: "Name is required" });
|
|
1609
|
+
} else if (byteLen(name) > MAX_NAME_LENGTH) {
|
|
1610
|
+
errors.push({ field: "name", message: `Name must be at most ${MAX_NAME_LENGTH} bytes` });
|
|
1611
|
+
}
|
|
1612
|
+
return errors;
|
|
1613
|
+
}
|
|
1614
|
+
function validateDescription(description) {
|
|
1615
|
+
const errors = [];
|
|
1616
|
+
if (!description || description.length === 0) {
|
|
1617
|
+
errors.push({ field: "description", message: "Description is required" });
|
|
1618
|
+
} else if (byteLen(description) > MAX_DESCRIPTION_LENGTH) {
|
|
1619
|
+
errors.push({
|
|
1620
|
+
field: "description",
|
|
1621
|
+
message: `Description must be at most ${MAX_DESCRIPTION_LENGTH} bytes`
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
return errors;
|
|
1625
|
+
}
|
|
1626
|
+
function validateExample(example, index) {
|
|
1627
|
+
const errors = [];
|
|
1628
|
+
if (!example || example.length === 0) {
|
|
1629
|
+
errors.push({ field: `examples[${index}]`, message: `Example ${index} is required` });
|
|
1630
|
+
} else if (byteLen(example) > MAX_EXAMPLE_LENGTH) {
|
|
1631
|
+
errors.push({
|
|
1632
|
+
field: `examples[${index}]`,
|
|
1633
|
+
message: `Example ${index} must be at most ${MAX_EXAMPLE_LENGTH} bytes`
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
return errors;
|
|
1637
|
+
}
|
|
1638
|
+
function validateExamples(examples) {
|
|
1639
|
+
const errors = [];
|
|
1640
|
+
if (examples.length < MIN_EXAMPLES) {
|
|
1641
|
+
errors.push({
|
|
1642
|
+
field: "examples",
|
|
1643
|
+
message: `At least ${MIN_EXAMPLES} examples required`
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
if (examples.length > MAX_EXAMPLES) {
|
|
1647
|
+
errors.push({
|
|
1648
|
+
field: "examples",
|
|
1649
|
+
message: `At most ${MAX_EXAMPLES} examples allowed`
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
for (let i = 0; i < examples.length; i++) {
|
|
1653
|
+
errors.push(...validateExample(examples[i], i));
|
|
1654
|
+
}
|
|
1655
|
+
return errors;
|
|
1656
|
+
}
|
|
1657
|
+
function validateTopic(topic) {
|
|
1658
|
+
const errors = [];
|
|
1659
|
+
errors.push(...validateName(topic.name));
|
|
1660
|
+
errors.push(...validateDescription(topic.description));
|
|
1661
|
+
errors.push(...validateExamples(topic.examples));
|
|
1662
|
+
const combined = byteLen(topic.name) + byteLen(topic.description) + topic.examples.reduce((sum, ex) => sum + byteLen(ex), 0);
|
|
1663
|
+
if (combined > MAX_COMBINED_LENGTH) {
|
|
1664
|
+
errors.push({
|
|
1665
|
+
field: "topic",
|
|
1666
|
+
message: `Combined length (${combined}) exceeds ${MAX_COMBINED_LENGTH} bytes`
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
return errors;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// src/core/metrics.ts
|
|
1673
|
+
function computeMetrics(results) {
|
|
1674
|
+
let truePositives = 0;
|
|
1675
|
+
let trueNegatives = 0;
|
|
1676
|
+
let falsePositives = 0;
|
|
1677
|
+
let falseNegatives = 0;
|
|
1678
|
+
let regressionCount = 0;
|
|
1679
|
+
for (const r of results) {
|
|
1680
|
+
if (r.testCase.expectedTriggered && r.actualTriggered) truePositives++;
|
|
1681
|
+
else if (!r.testCase.expectedTriggered && !r.actualTriggered) trueNegatives++;
|
|
1682
|
+
else if (!r.testCase.expectedTriggered && r.actualTriggered) falsePositives++;
|
|
1683
|
+
else if (r.testCase.expectedTriggered && !r.actualTriggered) falseNegatives++;
|
|
1684
|
+
if (r.testCase.source === "regression" && !r.correct) regressionCount++;
|
|
1685
|
+
}
|
|
1686
|
+
const totalPositives = truePositives + falseNegatives;
|
|
1687
|
+
const totalNegatives = trueNegatives + falsePositives;
|
|
1688
|
+
const total = results.length;
|
|
1689
|
+
const truePositiveRate = totalPositives > 0 ? truePositives / totalPositives : 0;
|
|
1690
|
+
const trueNegativeRate = totalNegatives > 0 ? trueNegatives / totalNegatives : 0;
|
|
1691
|
+
const accuracy = total > 0 ? (truePositives + trueNegatives) / total : 0;
|
|
1692
|
+
const coverage = Math.min(truePositiveRate, trueNegativeRate);
|
|
1693
|
+
const precision = truePositives + falsePositives > 0 ? truePositives / (truePositives + falsePositives) : 0;
|
|
1694
|
+
const recall = truePositiveRate;
|
|
1695
|
+
const f1Score = precision + recall > 0 ? 2 * precision * recall / (precision + recall) : 0;
|
|
1696
|
+
return {
|
|
1697
|
+
truePositives,
|
|
1698
|
+
trueNegatives,
|
|
1699
|
+
falsePositives,
|
|
1700
|
+
falseNegatives,
|
|
1701
|
+
truePositiveRate,
|
|
1702
|
+
trueNegativeRate,
|
|
1703
|
+
accuracy,
|
|
1704
|
+
coverage,
|
|
1705
|
+
f1Score,
|
|
1706
|
+
regressionCount
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function computeCategoryBreakdown(results) {
|
|
1710
|
+
const map = /* @__PURE__ */ new Map();
|
|
1711
|
+
for (const r of results) {
|
|
1712
|
+
const cat = r.testCase.category;
|
|
1713
|
+
const entry = map.get(cat) ?? { total: 0, fp: 0, fn: 0 };
|
|
1714
|
+
entry.total++;
|
|
1715
|
+
if (!r.testCase.expectedTriggered && r.actualTriggered) entry.fp++;
|
|
1716
|
+
if (r.testCase.expectedTriggered && !r.actualTriggered) entry.fn++;
|
|
1717
|
+
map.set(cat, entry);
|
|
1718
|
+
}
|
|
1719
|
+
return [...map.entries()].map(([category, { total, fp, fn }]) => ({
|
|
1720
|
+
category,
|
|
1721
|
+
total,
|
|
1722
|
+
fp,
|
|
1723
|
+
fn,
|
|
1724
|
+
errorRate: total > 0 ? (fp + fn) / total : 0
|
|
1725
|
+
})).sort((a, b) => b.errorRate - a.errorRate);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
export {
|
|
1729
|
+
SdkManagementService,
|
|
1730
|
+
getOrCreateManagementClient,
|
|
1731
|
+
SdkModelSecurityService,
|
|
1732
|
+
SdkPromptSetService,
|
|
1733
|
+
SdkRedTeamService,
|
|
1734
|
+
SdkRuntimeService,
|
|
1735
|
+
AirsScanService,
|
|
1736
|
+
RateLimitedScanService,
|
|
1737
|
+
sanitizeFilename,
|
|
1738
|
+
resolveOutputDir,
|
|
1739
|
+
writeBackupFile,
|
|
1740
|
+
readBackupFile,
|
|
1741
|
+
readBackupDir,
|
|
1742
|
+
ConfigSchema,
|
|
1743
|
+
resolveConfigFilePath,
|
|
1744
|
+
loadConfig,
|
|
1745
|
+
inspectConfig,
|
|
1746
|
+
validateName,
|
|
1747
|
+
validateDescription,
|
|
1748
|
+
validateExamples,
|
|
1749
|
+
validateTopic,
|
|
1750
|
+
computeMetrics,
|
|
1751
|
+
computeCategoryBreakdown
|
|
1752
|
+
};
|