Add interpreted results table to compare view
This commit is contained in:
@@ -36,7 +36,7 @@ export const ALERTS_TABLE_NAME = "alerts";
|
||||
export const GRAPH_TABLE_NAME = "graph";
|
||||
|
||||
export type RawTableResultSet = { t: "RawResultSet" } & RawResultSet;
|
||||
export type InterpretedResultSet<T> = {
|
||||
type InterpretedResultSet<T> = {
|
||||
t: "InterpretedResultSet";
|
||||
readonly schema: ResultSetSchema;
|
||||
name: string;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { Meta, StoryFn } from "@storybook/react";
|
||||
import { action } from "@storybook/addon-actions";
|
||||
|
||||
import { AlertTable as AlertTableComponent } from "../../view/results/AlertTable";
|
||||
|
||||
import "../../view/results/resultsView.css";
|
||||
import { AlertTableHeader } from "../../view/results/AlertTableHeader";
|
||||
import { AlertTableNoResults } from "../../view/results/AlertTableNoResults";
|
||||
|
||||
export default {
|
||||
title: "Results/Alert Table",
|
||||
@@ -17,443 +20,417 @@ const Template: StoryFn<typeof AlertTableComponent> = (args) => (
|
||||
|
||||
export const WithoutCodeFlows = Template.bind({});
|
||||
WithoutCodeFlows.args = {
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: { name: "alerts", rows: 1, columns: [] },
|
||||
name: "alerts",
|
||||
interpretation: {
|
||||
data: {
|
||||
version: "2.1.0",
|
||||
runs: [
|
||||
{
|
||||
tool: { driver: { name: "" } },
|
||||
results: [
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/Streams.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 98, startColumn: 35, endColumn: 37 },
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "1d25c2fbd979cbb:1",
|
||||
primaryLocationStartColumnFingerprint: "30",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/Streams.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 99, startColumn: 35, endColumn: 37 },
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "5c5ed8d70236498a:1",
|
||||
primaryLocationStartColumnFingerprint: "30",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 66,
|
||||
startColumn: 33,
|
||||
endLine: 68,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "bd306a1ab438981d:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 91,
|
||||
startColumn: 33,
|
||||
endLine: 93,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "b91980e3f3ee2a16:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 2,
|
||||
},
|
||||
region: {
|
||||
startLine: 100,
|
||||
startColumn: 49,
|
||||
endLine: 102,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "e4d69f1851f45b95:1",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 112,
|
||||
startColumn: 33,
|
||||
endLine: 114,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "f3fb11daf511ebdb:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 3,
|
||||
},
|
||||
region: {
|
||||
startLine: 84,
|
||||
startColumn: 42,
|
||||
endLine: 86,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "65a5e0f08a26f7fd:1",
|
||||
primaryLocationStartColumnFingerprint: "33",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 4,
|
||||
},
|
||||
region: {
|
||||
startLine: 157,
|
||||
startColumn: 42,
|
||||
endLine: 159,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "c7647299ca3416a7:1",
|
||||
primaryLocationStartColumnFingerprint: "33",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 5,
|
||||
},
|
||||
region: {
|
||||
startLine: 227,
|
||||
startColumn: 52,
|
||||
endLine: 228,
|
||||
endColumn: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "d86e48478bd5f82f:1",
|
||||
primaryLocationStartColumnFingerprint: "49",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/stream/JsonReader.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 6,
|
||||
},
|
||||
region: {
|
||||
startLine: 969,
|
||||
startColumn: 47,
|
||||
endLine: 971,
|
||||
endColumn: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "3bc8c477478d1d94:1",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/stream/JsonReader.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 6,
|
||||
},
|
||||
region: {
|
||||
startLine: 1207,
|
||||
startColumn: 47,
|
||||
endLine: 1209,
|
||||
endColumn: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "3bc8c477478d1d94:2",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
],
|
||||
results: [
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/Streams.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 98, startColumn: 35, endColumn: 37 },
|
||||
},
|
||||
],
|
||||
t: "SarifInterpretationData",
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "1d25c2fbd979cbb:1",
|
||||
primaryLocationStartColumnFingerprint: "30",
|
||||
},
|
||||
sourceLocationPrefix: "/home/runner/work/gson/gson",
|
||||
numTruncatedResults: 0,
|
||||
numTotalResults: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/Streams.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 99, startColumn: 35, endColumn: 37 },
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "5c5ed8d70236498a:1",
|
||||
primaryLocationStartColumnFingerprint: "30",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 66,
|
||||
startColumn: 33,
|
||||
endLine: 68,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "bd306a1ab438981d:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 91,
|
||||
startColumn: 33,
|
||||
endLine: 93,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "b91980e3f3ee2a16:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 2,
|
||||
},
|
||||
region: {
|
||||
startLine: 100,
|
||||
startColumn: 49,
|
||||
endLine: 102,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "e4d69f1851f45b95:1",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 1,
|
||||
},
|
||||
region: {
|
||||
startLine: 112,
|
||||
startColumn: 33,
|
||||
endLine: 114,
|
||||
endColumn: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "f3fb11daf511ebdb:1",
|
||||
primaryLocationStartColumnFingerprint: "28",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 3,
|
||||
},
|
||||
region: {
|
||||
startLine: 84,
|
||||
startColumn: 42,
|
||||
endLine: 86,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "65a5e0f08a26f7fd:1",
|
||||
primaryLocationStartColumnFingerprint: "33",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 4,
|
||||
},
|
||||
region: {
|
||||
startLine: 157,
|
||||
startColumn: 42,
|
||||
endLine: 159,
|
||||
endColumn: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "c7647299ca3416a7:1",
|
||||
primaryLocationStartColumnFingerprint: "33",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 5,
|
||||
},
|
||||
region: {
|
||||
startLine: 227,
|
||||
startColumn: 52,
|
||||
endLine: 228,
|
||||
endColumn: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "d86e48478bd5f82f:1",
|
||||
primaryLocationStartColumnFingerprint: "49",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/stream/JsonReader.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 6,
|
||||
},
|
||||
region: {
|
||||
startLine: 969,
|
||||
startColumn: 47,
|
||||
endLine: 971,
|
||||
endColumn: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "3bc8c477478d1d94:1",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
{
|
||||
ruleId: "java/example/empty-block",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/example/empty-block", index: 0 },
|
||||
message: { text: "This is a empty block." },
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "gson/src/main/java/com/google/gson/stream/JsonReader.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 6,
|
||||
},
|
||||
region: {
|
||||
startLine: 1207,
|
||||
startColumn: 47,
|
||||
endLine: 1209,
|
||||
endColumn: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "3bc8c477478d1d94:2",
|
||||
primaryLocationStartColumnFingerprint: "40",
|
||||
},
|
||||
},
|
||||
],
|
||||
sourceLocationPrefix: "/home/runner/work/gson/gson",
|
||||
numTruncatedResults: 0,
|
||||
databaseUri: "file:///a/b/c/java",
|
||||
resultsPath: "file:///a/b/c/results.sarif",
|
||||
nonemptyRawResults: true,
|
||||
offset: 0,
|
||||
header: <AlertTableHeader sortState={undefined} />,
|
||||
noResults: (
|
||||
<AlertTableNoResults
|
||||
nonemptyRawResults={true}
|
||||
showRawResults={() => action("show-raw-results")}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithCodeFlows = Template.bind({});
|
||||
WithCodeFlows.args = {
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: { name: "alerts", rows: 1, columns: [] },
|
||||
name: "alerts",
|
||||
interpretation: {
|
||||
data: {
|
||||
version: "2.1.0",
|
||||
runs: [
|
||||
{
|
||||
tool: { driver: { name: "" } },
|
||||
results: [
|
||||
{
|
||||
ruleId: "java/sql-injection",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/sql-injection", index: 0 },
|
||||
message: {
|
||||
text: "This query depends on a [user-provided value](1).",
|
||||
},
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 15, startColumn: 29, endColumn: 56 },
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "87e2d3cc5b365094:1",
|
||||
primaryLocationStartColumnFingerprint: "16",
|
||||
},
|
||||
codeFlows: [
|
||||
{
|
||||
threadFlows: [
|
||||
{
|
||||
locations: [
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: {
|
||||
startLine: 13,
|
||||
startColumn: 25,
|
||||
endColumn: 54,
|
||||
},
|
||||
},
|
||||
message: { text: "id : String" },
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "file:/",
|
||||
index: 5,
|
||||
},
|
||||
region: {
|
||||
startLine: 13,
|
||||
startColumn: 25,
|
||||
endColumn: 54,
|
||||
},
|
||||
},
|
||||
message: { text: "id : String" },
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: {
|
||||
startLine: 15,
|
||||
startColumn: 29,
|
||||
endColumn: 56,
|
||||
},
|
||||
},
|
||||
message: { text: "... + ..." },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relatedLocations: [
|
||||
{
|
||||
id: 1,
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 13, startColumn: 25, endColumn: 54 },
|
||||
},
|
||||
message: { text: "user-provided value" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
t: "SarifInterpretationData",
|
||||
results: [
|
||||
{
|
||||
ruleId: "java/sql-injection",
|
||||
ruleIndex: 0,
|
||||
rule: { id: "java/sql-injection", index: 0 },
|
||||
message: {
|
||||
text: "This query depends on a [user-provided value](1).",
|
||||
},
|
||||
sourceLocationPrefix: "/home/runner/work/sql2o-example/sql2o-example",
|
||||
numTruncatedResults: 0,
|
||||
numTotalResults: 1,
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 15, startColumn: 29, endColumn: 56 },
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "87e2d3cc5b365094:1",
|
||||
primaryLocationStartColumnFingerprint: "16",
|
||||
},
|
||||
codeFlows: [
|
||||
{
|
||||
threadFlows: [
|
||||
{
|
||||
locations: [
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: {
|
||||
startLine: 13,
|
||||
startColumn: 25,
|
||||
endColumn: 54,
|
||||
},
|
||||
},
|
||||
message: { text: "id : String" },
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "file:/",
|
||||
index: 5,
|
||||
},
|
||||
region: {
|
||||
startLine: 13,
|
||||
startColumn: 25,
|
||||
endColumn: 54,
|
||||
},
|
||||
},
|
||||
message: { text: "id : String" },
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: {
|
||||
startLine: 15,
|
||||
startColumn: 29,
|
||||
endColumn: 56,
|
||||
},
|
||||
},
|
||||
message: { text: "... + ..." },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relatedLocations: [
|
||||
{
|
||||
id: 1,
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/main/java/org/example/HelloController.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 0,
|
||||
},
|
||||
region: { startLine: 13, startColumn: 25, endColumn: 54 },
|
||||
},
|
||||
message: { text: "user-provided value" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
sourceLocationPrefix: "/home/runner/work/sql2o-example/sql2o-example",
|
||||
numTruncatedResults: 0,
|
||||
databaseUri: "file:///a/b/c/java",
|
||||
resultsPath: "file:///a/b/c/results.sarif",
|
||||
nonemptyRawResults: true,
|
||||
offset: 0,
|
||||
header: <AlertTableHeader sortState={undefined} />,
|
||||
noResults: (
|
||||
<AlertTableNoResults
|
||||
nonemptyRawResults={true}
|
||||
showRawResults={() => action("show-raw-results")}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import { vscode } from "../vscode-api";
|
||||
import TextButton from "../common/TextButton";
|
||||
import { styled } from "styled-components";
|
||||
import { RawCompareResultTable } from "./RawCompareResultTable";
|
||||
import { InterpretedCompareResultTable } from "./InterpretedCompareResultTable";
|
||||
|
||||
interface Props {
|
||||
queryInfo: SetComparisonQueryInfoMessage;
|
||||
@@ -76,6 +77,13 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
{result.kind === "interpreted" && (
|
||||
<InterpretedCompareResultTable
|
||||
results={result.from}
|
||||
databaseUri={queryInfo.databaseUri}
|
||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{result.kind === "raw" && (
|
||||
@@ -87,6 +95,13 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
{result.kind === "interpreted" && (
|
||||
<InterpretedCompareResultTable
|
||||
results={result.to}
|
||||
databaseUri={queryInfo.databaseUri}
|
||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import * as React from "react";
|
||||
import * as sarif from "sarif";
|
||||
import { AlertTable } from "../results/AlertTable";
|
||||
|
||||
type Props = {
|
||||
results: sarif.Result[];
|
||||
databaseUri: string;
|
||||
sourceLocationPrefix: string;
|
||||
};
|
||||
|
||||
export const InterpretedCompareResultTable = ({
|
||||
results,
|
||||
databaseUri,
|
||||
sourceLocationPrefix,
|
||||
}: Props) => {
|
||||
return (
|
||||
<AlertTable
|
||||
results={results}
|
||||
databaseUri={databaseUri}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
header={
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2}></th>
|
||||
<th className={`vscode-codeql__alert-message-cell`} colSpan={3}>
|
||||
Message
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,34 +1,34 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import * as Keys from "./result-keys";
|
||||
import {
|
||||
className,
|
||||
ResultTableProps,
|
||||
jumpToLocation,
|
||||
} from "./result-table-utils";
|
||||
import { className, jumpToLocation } from "./result-table-utils";
|
||||
import { onNavigation } from "./ResultsApp";
|
||||
import {
|
||||
InterpretedResultSet,
|
||||
NavigateMsg,
|
||||
NavigationDirection,
|
||||
SarifInterpretationData,
|
||||
} from "../../common/interface-types";
|
||||
import { NavigateMsg, NavigationDirection } from "../../common/interface-types";
|
||||
import { parseSarifLocation, isNoLocation } from "../../common/sarif-utils";
|
||||
import { sendTelemetry } from "../common/telemetry";
|
||||
import { AlertTableHeader } from "./AlertTableHeader";
|
||||
import { AlertTableNoResults } from "./AlertTableNoResults";
|
||||
import { AlertTableTruncatedMessage } from "./AlertTableTruncatedMessage";
|
||||
import { AlertTableResultRow } from "./AlertTableResultRow";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useScrollIntoView } from "./useScrollIntoView";
|
||||
|
||||
type AlertTableProps = ResultTableProps & {
|
||||
resultSet: InterpretedResultSet<SarifInterpretationData>;
|
||||
type Props = {
|
||||
results: Sarif.Result[];
|
||||
databaseUri: string;
|
||||
sourceLocationPrefix: string;
|
||||
numTruncatedResults?: number;
|
||||
|
||||
header: ReactNode;
|
||||
noResults?: ReactNode;
|
||||
};
|
||||
|
||||
export function AlertTable(props: AlertTableProps) {
|
||||
const { databaseUri, resultSet } = props;
|
||||
|
||||
export function AlertTable({
|
||||
results,
|
||||
databaseUri,
|
||||
sourceLocationPrefix,
|
||||
numTruncatedResults,
|
||||
header,
|
||||
noResults,
|
||||
}: Props) {
|
||||
const [expanded, setExpanded] = useState<Set<string>>(new Set<string>());
|
||||
const [selectedItem, setSelectedItem] = useState<Keys.ResultKey | undefined>(
|
||||
undefined,
|
||||
@@ -108,22 +108,21 @@ export function AlertTable(props: AlertTableProps) {
|
||||
const handleNavigationEvent = useCallback(
|
||||
(event: NavigateMsg) => {
|
||||
const key = getNewSelection(selectedItem, event.direction);
|
||||
const data = resultSet.interpretation.data;
|
||||
|
||||
// Check if the selected node actually exists (bounds check) and get its location if relevant
|
||||
let jumpLocation: Sarif.Location | undefined;
|
||||
if (key.pathNodeIndex !== undefined) {
|
||||
jumpLocation = Keys.getPathNode(data, key);
|
||||
jumpLocation = Keys.getPathNode(results, key);
|
||||
if (jumpLocation === undefined) {
|
||||
return; // Result does not exist
|
||||
}
|
||||
} else if (key.pathIndex !== undefined) {
|
||||
if (Keys.getPath(data, key) === undefined) {
|
||||
if (Keys.getPath(results, key) === undefined) {
|
||||
return; // Path does not exist
|
||||
}
|
||||
jumpLocation = undefined; // When selecting a 'path', don't jump anywhere.
|
||||
} else {
|
||||
jumpLocation = Keys.getResult(data, key)?.locations?.[0];
|
||||
jumpLocation = Keys.getResult(results, key)?.locations?.[0];
|
||||
if (jumpLocation === undefined) {
|
||||
return; // Path step does not exist.
|
||||
}
|
||||
@@ -131,7 +130,7 @@ export function AlertTable(props: AlertTableProps) {
|
||||
if (jumpLocation !== undefined) {
|
||||
const parsedLocation = parseSarifLocation(
|
||||
jumpLocation,
|
||||
resultSet.interpretation.sourceLocationPrefix,
|
||||
sourceLocationPrefix,
|
||||
);
|
||||
if (!isNoLocation(parsedLocation)) {
|
||||
jumpToLocation(parsedLocation, databaseUri);
|
||||
@@ -162,7 +161,7 @@ export function AlertTable(props: AlertTableProps) {
|
||||
setExpanded(newExpanded);
|
||||
setSelectedItem(key);
|
||||
},
|
||||
[databaseUri, expanded, resultSet, selectedItem],
|
||||
[databaseUri, expanded, results, sourceLocationPrefix, selectedItem],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -172,9 +171,6 @@ export function AlertTable(props: AlertTableProps) {
|
||||
};
|
||||
}, [handleNavigationEvent]);
|
||||
|
||||
const { numTruncatedResults, sourceLocationPrefix } =
|
||||
resultSet.interpretation;
|
||||
|
||||
const updateSelectionCallback = useCallback(
|
||||
(resultKey: Keys.PathNode | Keys.Result | undefined) => {
|
||||
setSelectedItem(resultKey);
|
||||
@@ -183,31 +179,33 @@ export function AlertTable(props: AlertTableProps) {
|
||||
[],
|
||||
);
|
||||
|
||||
if (!resultSet.interpretation.data.runs?.[0]?.results?.length) {
|
||||
return <AlertTableNoResults {...props} />;
|
||||
if (!results?.length) {
|
||||
return <>{noResults}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<table className={className}>
|
||||
<AlertTableHeader sortState={resultSet.interpretation.data.sortState} />
|
||||
{header}
|
||||
<tbody>
|
||||
{resultSet.interpretation.data.runs[0].results.map(
|
||||
(result, resultIndex) => (
|
||||
<AlertTableResultRow
|
||||
key={resultIndex}
|
||||
result={result}
|
||||
resultIndex={resultIndex}
|
||||
expanded={expanded}
|
||||
selectedItem={selectedItem}
|
||||
selectedItemRef={selectedItemRef}
|
||||
databaseUri={databaseUri}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
updateSelectionCallback={updateSelectionCallback}
|
||||
toggleExpanded={toggle}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AlertTableTruncatedMessage numTruncatedResults={numTruncatedResults} />
|
||||
{results.map((result, resultIndex) => (
|
||||
<AlertTableResultRow
|
||||
key={resultIndex}
|
||||
result={result}
|
||||
resultIndex={resultIndex}
|
||||
expanded={expanded}
|
||||
selectedItem={selectedItem}
|
||||
selectedItemRef={selectedItemRef}
|
||||
databaseUri={databaseUri}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
updateSelectionCallback={updateSelectionCallback}
|
||||
toggleExpanded={toggle}
|
||||
/>
|
||||
))}
|
||||
{numTruncatedResults ? (
|
||||
<AlertTableTruncatedMessage
|
||||
numTruncatedResults={numTruncatedResults}
|
||||
/>
|
||||
) : undefined}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { AlertTable } from "./AlertTable";
|
||||
import { Graph } from "./Graph";
|
||||
import { RawTable } from "./RawTable";
|
||||
import { ResultTableProps } from "./result-table-utils";
|
||||
import { AlertTableNoResults } from "./AlertTableNoResults";
|
||||
import { AlertTableHeader } from "./AlertTableHeader";
|
||||
|
||||
export function ResultTable(props: ResultTableProps) {
|
||||
const { resultSet } = props;
|
||||
@@ -13,11 +15,23 @@ export function ResultTable(props: ResultTableProps) {
|
||||
const data = resultSet.interpretation.data;
|
||||
switch (data.t) {
|
||||
case "SarifInterpretationData": {
|
||||
const sarifResultSet = {
|
||||
...resultSet,
|
||||
interpretation: { ...resultSet.interpretation, data },
|
||||
};
|
||||
return <AlertTable {...props} resultSet={sarifResultSet} />;
|
||||
return (
|
||||
<AlertTable
|
||||
results={data.runs[0].results ?? []}
|
||||
databaseUri={props.databaseUri}
|
||||
sourceLocationPrefix={
|
||||
resultSet.interpretation.sourceLocationPrefix
|
||||
}
|
||||
numTruncatedResults={resultSet.interpretation.numTruncatedResults}
|
||||
header={<AlertTableHeader sortState={data.sortState} />}
|
||||
noResults={
|
||||
<AlertTableNoResults
|
||||
nonemptyRawResults={props.nonemptyRawResults}
|
||||
showRawResults={props.showRawResults}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "GraphInterpretationData": {
|
||||
return (
|
||||
|
||||
@@ -40,20 +40,20 @@ export type ResultKey = Result | Path | PathNode;
|
||||
* Looks up a specific result in a result set.
|
||||
*/
|
||||
export function getResult(
|
||||
sarif: sarif.Log,
|
||||
results: sarif.Result[],
|
||||
key: Result | Path | PathNode,
|
||||
): sarif.Result | undefined {
|
||||
return sarif.runs[0]?.results?.[key.resultIndex];
|
||||
return results[key.resultIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a specific path in a result set.
|
||||
*/
|
||||
export function getPath(
|
||||
sarif: sarif.Log,
|
||||
results: sarif.Result[],
|
||||
key: Path | PathNode,
|
||||
): sarif.ThreadFlow | undefined {
|
||||
const result = getResult(sarif, key);
|
||||
const result = getResult(results, key);
|
||||
if (result === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -76,10 +76,10 @@ export function getPath(
|
||||
* Looks up a specific path node in a result set.
|
||||
*/
|
||||
export function getPathNode(
|
||||
sarif: sarif.Log,
|
||||
results: sarif.Result[],
|
||||
key: PathNode,
|
||||
): sarif.Location | undefined {
|
||||
const path = getPath(sarif, key);
|
||||
const path = getPath(results, key);
|
||||
if (path === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user