Skip to content

Commit

Permalink
Merge pull request #3687 from p-/display-max-steps-local-path-query
Browse files Browse the repository at this point in the history
Display length of shortest path in local results UI
  • Loading branch information
aeisenberg authored Aug 12, 2024
2 parents 96c33a1 + 268fd9f commit 756200b
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 4 deletions.
2 changes: 2 additions & 0 deletions extensions/ql-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [UNRELEASED]

- Update results view to display the length of the shortest path for path queries. [#3687](https://github.com/github/vscode-codeql/pull/3687)

## 1.14.0 - 7 August 2024

- Add Python support to the CodeQL Model Editor. [#3676](https://github.com/github/vscode-codeql/pull/3676)
Expand Down
5 changes: 3 additions & 2 deletions extensions/ql-vscode/src/view/results/AlertTablePathRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCe
import { useCallback, useMemo } from "react";
import { VerticalRule } from "../common/VerticalRule";
import type { UserSettings } from "../../common/interface-types";
import { pluralize } from "../../common/word";

interface Props {
export interface Props {
path: ThreadFlow;
pathIndex: number;
resultIndex: number;
Expand Down Expand Up @@ -65,7 +66,7 @@ export function AlertTablePathRow(props: Props) {
onClick={handleDropdownClick}
/>
<td className="vscode-codeql__text-center" colSpan={4}>
Path
{`Path (${pluralize(path.locations.length, "step", "steps")})`}
</td>
</tr>
{currentPathExpanded &&
Expand Down
13 changes: 11 additions & 2 deletions extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import { SarifLocation } from "./locations/SarifLocation";
import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations";
import { AlertTablePathRow } from "./AlertTablePathRow";
import type { UserSettings } from "../../common/interface-types";
import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react";

interface Props {
export interface Props {
result: Result;
resultIndex: number;
expanded: Set<string>;
Expand Down Expand Up @@ -83,6 +84,11 @@ export function AlertTableResultRow(props: Props) {
/>
);

const allPaths = getAllPaths(result);
const shortestPath = Math.min(
...allPaths.map((path) => path.locations.length),
);

const currentResultExpanded = expanded.has(keyToString(resultKey));
return (
<>
Expand All @@ -102,6 +108,9 @@ export function AlertTableResultRow(props: Props) {
onClick={handleDropdownClick}
/>
<td className="vscode-codeql__icon-cell">{listUnordered}</td>
<td className="vscode-codeql__icon-cell">
<VSCodeBadge title="Shortest path">{shortestPath}</VSCodeBadge>
</td>
<td colSpan={3}>{msg}</td>
</>
)}
Expand All @@ -118,7 +127,7 @@ export function AlertTableResultRow(props: Props) {
</tr>
{currentResultExpanded &&
result.codeFlows &&
getAllPaths(result).map((path, pathIndex) => (
allPaths.map((path, pathIndex) => (
<AlertTablePathRow
key={`${resultIndex}-${pathIndex}`}
{...props}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { render as reactRender, screen } from "@testing-library/react";
import type { Props } from "../AlertTablePathRow";
import { AlertTablePathRow } from "../AlertTablePathRow";
import { createMockResults } from "../../../../test/factories/results/mockresults";

describe(AlertTablePathRow.name, () => {
const render = (props?: Props) => {
const mockRef = { current: null } as React.RefObject<HTMLTableRowElement>;
const results = createMockResults();
const threadFlow = results[0]?.codeFlows?.[0]?.threadFlows?.[0];

if (!threadFlow) {
throw new Error("ThreadFlow is undefined");
}
reactRender(
<AlertTablePathRow
resultIndex={1}
selectedItem={undefined}
selectedItemRef={mockRef}
path={threadFlow}
pathIndex={0}
currentPathExpanded={true}
databaseUri={"dbUri"}
sourceLocationPrefix="src"
userSettings={{ shouldShowProvenance: false }}
updateSelectionCallback={jest.fn()}
toggleExpanded={jest.fn()}
{...props}
/>,
);
};

it("renders number of steps", () => {
render();

expect(screen.getByText("Path (3 steps)")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { render as reactRender, screen } from "@testing-library/react";
import { AlertTableResultRow } from "../AlertTableResultRow";
import type { Props } from "../AlertTableResultRow";
import { createMockResults } from "../../../../test/factories/results/mockresults";

describe(AlertTableResultRow.name, () => {
const render = (props?: Props) => {
const mockRef = { current: null } as React.RefObject<HTMLTableRowElement>;
const results = createMockResults();

reactRender(
<AlertTableResultRow
result={results[0]}
expanded={new Set()}
resultIndex={1}
selectedItem={undefined}
selectedItemRef={mockRef}
databaseUri={"dbUri"}
sourceLocationPrefix="src"
userSettings={{ shouldShowProvenance: false }}
updateSelectionCallback={jest.fn()}
toggleExpanded={jest.fn()}
{...props}
/>,
);
};

it("renders shortest path badge", () => {
render();

expect(screen.getByTitle("Shortest path")).toHaveTextContent("3");
});
});
104 changes: 104 additions & 0 deletions extensions/ql-vscode/test/factories/results/mockresults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { Result } from "sarif";

export function createMockResults(): Result[] {
return [
{
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" },
},
],
},
];
}

0 comments on commit 756200b

Please sign in to comment.