feat: export skillspector issue details (#2430)

This commit is contained in:
Patrick Erichsen
2026-05-28 10:32:28 -05:00
committed by GitHub
parent 0132f1f530
commit 51967bca7f
3 changed files with 41 additions and 1 deletions
+1
View File
@@ -359,6 +359,7 @@ function normalizeSkillSpectorAnalysis(analysis: StoredSkillSpectorAnalysis) {
issueCount: analysis.issueCount,
issues: analysis.issues.map((issue) => ({
issueId: issue.issueId,
category: issue.category ?? null,
severity: issue.severity,
confidence: issue.confidence ?? null,
explanation: issue.explanation,
+15 -1
View File
@@ -61,9 +61,11 @@ const baseArtifact: ArtifactExportInput = {
issues: [
{
issueId: "SDI-1",
category: "Sensitive Data Exposure",
severity: "HIGH",
confidence: 0.98,
explanation: "The skill body does not match the declared purpose.",
explanation:
"The skill body does not match the declared purpose and mentions token=supersecret123.",
},
],
},
@@ -175,7 +177,19 @@ describe("security dataset normalizer", () => {
scanner_version: "skillspector-v2.0.0",
status: "suspicious",
verdict: "DO_NOT_INSTALL",
score: 55,
severity: "HIGH",
reason_codes: ["SDI-1"],
issues: [
{
code: "SDI-1",
category: "Sensitive Data Exposure",
severity: "HIGH",
confidence: 0.98,
explanation_redacted:
"The skill body does not match the declared purpose and mentions [REDACTED_SECRET]",
},
],
raw_status_family: "suspicious",
});
expect(rows.staticFindings[0]).toMatchObject({
+25
View File
@@ -58,6 +58,7 @@ export type SkillSpectorAnalysisInput = {
issueCount: number;
issues: Array<{
issueId: string;
category?: string | null;
severity: string;
confidence: number | null;
explanation: string;
@@ -173,10 +174,19 @@ export type ScanResultRow = {
verdict: string | null;
confidence: string | null;
checked_at: number | null;
score?: number | null;
severity?: string | null;
reason_codes: string[];
engine_stats: VtAnalysisInput["engineStats"];
summary_redacted: string | null;
raw_status_family: DatasetLabel;
issues?: Array<{
code: string;
category: string | null;
severity: string;
confidence: number | null;
explanation_redacted: string | null;
}>;
};
export type StaticFindingRow = {
@@ -408,9 +418,12 @@ function buildScanResultRows(input: ArtifactExportInput, artifactId: string): Sc
verdict: input.skillSpectorAnalysis.recommendation,
confidence: null,
checked_at: input.skillSpectorAnalysis.checkedAt,
score: input.skillSpectorAnalysis.score,
severity: input.skillSpectorAnalysis.severity,
reason_codes: input.skillSpectorAnalysis.issues
.map((issue) => issue.issueId)
.sort((a, b) => a.localeCompare(b)),
issues: normalizeSkillSpectorIssues(input.skillSpectorAnalysis.issues),
engine_stats: null,
summary_redacted: redactText(
input.skillSpectorAnalysis.summary ?? input.skillSpectorAnalysis.error,
@@ -473,6 +486,18 @@ function buildStaticFindingRows(
}));
}
function normalizeSkillSpectorIssues(issues: SkillSpectorAnalysisInput["issues"]) {
return issues
.map((issue) => ({
code: issue.issueId,
category: issue.category ?? null,
severity: issue.severity,
confidence: issue.confidence ?? null,
explanation_redacted: redactText(issue.explanation),
}))
.sort((left, right) => left.code.localeCompare(right.code));
}
function buildClawScanFindingRows(
input: ArtifactExportInput,
artifactId: string,