Skip to content

Commit 279fe57

Browse files
kantordalex-mcgovernstacklokbotdependabot[bot]peppescg
authored
feat: support PII on the dashboard (#326)
* test: make tabs-messages tests parametric * filter table by pii alert type * chore(main): release 0.16.0 (#325) * chore(deps-dev): bump @types/react-dom from 19.0.2 to 19.0.3 (#330) Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 19.0.2 to 19.0.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: audit vuln deps (#332) * implement pii summary icon * add empty state for piiii * add pii to conversation page * fix type error --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Alex McGovern <58784948+alex-mcgovern@users.noreply.github.com> Co-authored-by: Stacklok Bot <140063061+stacklokbot@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Giuseppe Scuglia <peppescg@gmail.com>
1 parent 73c55a2 commit 279fe57

14 files changed

+200
-56
lines changed

src/constants/empty-state-strings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const emptyStateStrings = {
77
anErrorOccurred: 'An error occurred',
88
noLeakedSecretsDetected: 'No leaked secrets detected',
99
noMaliciousPackagesDetected: 'No malicious packages detected',
10+
noPIIDetected:
11+
'No leaked personally identifiable information (PII) detected',
1012
noSearchResultsFor: (x: string | undefined): string =>
1113
!x ? 'No search results' : `No search results for "${x}"`,
1214
},
@@ -22,6 +24,8 @@ export const emptyStateStrings = {
2224
'Messages are issues that CodeGate has detected and mitigated in your interactions with the LLM.',
2325
secretsDesc:
2426
'CodeGate helps you protect sensitive information from being accidentally exposed to AI models and third-party AI provider systems by redacting detected secrets from your prompts using encryption.',
27+
piiDesc:
28+
'CodeGate helps you protect sensitive personally identifiable information (PII) from being accidentally exposed to AI models and third-party AI provider systems by redacting detected PII from your prompts using encryption.',
2529
maliciousDesc:
2630
"CodeGate's dependency risk insight helps protect your codebase from malicious or vulnerable dependencies. It identifies potentially risky packages and suggests fixed versions or alternative packages to consider.",
2731
},

src/features/dashboard-messages/components/__tests__/table-messages.alerts.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ it('shows zero in alerts counts when no alerts', async () => {
3333
name: /secrets count/i,
3434
})
3535
).toHaveTextContent('0')
36+
expect(
37+
screen.getByRole('button', {
38+
name: /personally identifiable information.*count/i,
39+
})
40+
).toHaveTextContent('0')
3641
})
3742

3843
it('shows count of malicious alerts in row', async () => {
@@ -80,3 +85,26 @@ it('shows count of secret alerts in row', async () => {
8085
})
8186
).toHaveTextContent('10')
8287
})
88+
89+
it('shows count of pii alerts in row', async () => {
90+
server.use(
91+
http.get(mswEndpoint('/api/v1/workspaces/:workspace_name/messages'), () =>
92+
HttpResponse.json([
93+
mockConversation({
94+
alertsConfig: { numAlerts: 10, type: 'pii' },
95+
}),
96+
])
97+
)
98+
)
99+
render(<TableMessages />)
100+
101+
await waitFor(() => {
102+
expect(screen.queryByText(/loading.../i)).not.toBeInTheDocument()
103+
})
104+
105+
expect(
106+
screen.getByRole('button', {
107+
name: /pii/i,
108+
})
109+
).toHaveTextContent('10')
110+
})

src/features/dashboard-messages/components/__tests__/table-messages.empty-state.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,48 @@ const TEST_CASES: TestCase[] = [
304304
actions: null,
305305
},
306306
},
307+
{
308+
testDescription: 'Has alerts, view is "pii"',
309+
handlers: [
310+
http.get(mswEndpoint('/api/v1/workspaces'), () => {
311+
return HttpResponse.json({
312+
workspaces: [
313+
{
314+
name: 'default',
315+
is_active: true,
316+
},
317+
{
318+
name: 'foo-bar',
319+
is_active: false,
320+
},
321+
],
322+
})
323+
}),
324+
http.get(mswEndpoint('/api/v1/workspaces/archive'), () => {
325+
return HttpResponse.json({
326+
workspaces: [],
327+
})
328+
}),
329+
http.get(
330+
mswEndpoint('/api/v1/workspaces/:workspace_name/messages'),
331+
() => {
332+
return HttpResponse.json(
333+
Array.from({ length: 10 }).map(() => mockAlert({ type: 'pii' }))
334+
)
335+
}
336+
),
337+
],
338+
searchParams: {
339+
view: AlertsFilterView.PII,
340+
search: null,
341+
},
342+
expected: {
343+
title: emptyStateStrings.title.noPIIDetected,
344+
body: emptyStateStrings.body.piiDesc,
345+
illustrationTestId: IllustrationTestId.DONE,
346+
actions: null,
347+
},
348+
},
307349
]
308350

309351
test.each(TEST_CASES)('$testDescription', async (testCase) => {

src/features/dashboard-messages/components/__tests__/tabs-messages.test.tsx

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -40,56 +40,42 @@ test('shows correct count of all packages', async () => {
4040
})
4141
})
4242

43-
test('shows correct count of malicious packages', async () => {
44-
server.use(
45-
http.get(mswEndpoint('/api/v1/workspaces/:workspace_name/messages'), () => {
46-
return HttpResponse.json(
47-
Array.from({ length: 13 }).map(() =>
48-
mockConversation({
49-
alertsConfig: {
50-
type: 'malicious',
51-
numAlerts: 1,
52-
},
53-
})
54-
)
43+
const filteredCases = [
44+
{ tabLabel: /malicious/i, alertType: 'malicious' as const, count: 13 },
45+
{ tabLabel: /secrets/i, alertType: 'secret' as const, count: 10 },
46+
{ tabLabel: /pii/i, alertType: 'pii' as const, count: 9 },
47+
]
48+
49+
filteredCases.forEach(({ tabLabel, alertType, count }) => {
50+
test(`shows correct count of ${alertType} packages`, async () => {
51+
server.use(
52+
http.get(
53+
mswEndpoint('/api/v1/workspaces/:workspace_name/messages'),
54+
() => {
55+
return HttpResponse.json(
56+
Array.from({ length: count }).map(() =>
57+
mockConversation({
58+
alertsConfig: {
59+
type: alertType,
60+
numAlerts: 1,
61+
},
62+
})
63+
)
64+
)
65+
}
5566
)
56-
})
57-
)
67+
)
5868

59-
const { getByRole } = render(
60-
<TabsMessages>
61-
<div>foo</div>
62-
</TabsMessages>
63-
)
69+
const { getByRole } = render(
70+
<TabsMessages>
71+
<div>foo</div>
72+
</TabsMessages>
73+
)
6474

65-
await waitFor(() => {
66-
expect(getByRole('tab', { name: /malicious/i })).toHaveTextContent('13')
67-
})
68-
})
69-
70-
test('shows correct count of secret packages', async () => {
71-
server.use(
72-
http.get(mswEndpoint('/api/v1/workspaces/:workspace_name/messages'), () => {
73-
return HttpResponse.json(
74-
Array.from({ length: 13 }).map(() =>
75-
mockConversation({
76-
alertsConfig: {
77-
type: 'secret',
78-
numAlerts: 1,
79-
},
80-
})
81-
)
75+
await waitFor(() => {
76+
expect(getByRole('tab', { name: tabLabel })).toHaveTextContent(
77+
String(count)
8278
)
8379
})
84-
)
85-
86-
const { getByRole } = render(
87-
<TabsMessages>
88-
<div>foo</div>
89-
</TabsMessages>
90-
)
91-
92-
await waitFor(() => {
93-
expect(getByRole('tab', { name: /secrets/i })).toHaveTextContent('13')
9480
})
9581
})

src/features/dashboard-messages/components/conversation-summary.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Hash01,
1313
Key01,
1414
PackageX,
15+
Passport,
1516
Server05,
1617
} from '@untitled-ui/icons-react'
1718

@@ -51,8 +52,8 @@ function AlertsSummaryCount({
5152
}: {
5253
count: number
5354
type: {
54-
singular: 'malicious package' | 'secret'
55-
plural: 'malicious packages' | 'secrets'
55+
singular: string
56+
plural: string
5657
}
5758
}) {
5859
const typeText = count === 1 ? type.singular : type.plural
@@ -97,9 +98,9 @@ export function ConversationSummary({
9798
}: {
9899
conversation: Conversation
99100
}) {
100-
const { malicious, secrets } = conversation.alerts
101+
const { malicious, secrets, pii } = conversation.alerts
101102
? countConversationAlerts(conversation.alerts)
102-
: { malicious: 0, secrets: 0 }
103+
: { malicious: 0, secrets: 0, pii: 0 }
103104

104105
return (
105106
<div className="flex gap-4">
@@ -166,6 +167,19 @@ export function ConversationSummary({
166167
/>
167168
}
168169
/>
170+
<ConversationSummaryListItem
171+
icon={Passport}
172+
title="PII"
173+
value={
174+
<AlertsSummaryCount
175+
type={{
176+
singular: 'personally identifiable information',
177+
plural: 'personally identifiable information',
178+
}}
179+
count={pii}
180+
/>
181+
}
182+
/>
169183
</ConversationSummaryList>
170184
</div>
171185
)

src/features/dashboard-messages/components/table-messages-empty-state.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ function EmptyStateSecrets() {
120120
)
121121
}
122122

123+
function EmptyStatePII() {
124+
return (
125+
<EmptyState
126+
title={emptyStateStrings.title.noPIIDetected}
127+
body={emptyStateStrings.body.piiDesc}
128+
illustration={IllustrationDone}
129+
actions={null}
130+
/>
131+
)
132+
}
133+
123134
export function EmptyStateError() {
124135
return (
125136
<EmptyState
@@ -209,6 +220,15 @@ export function TableMessagesEmptyState() {
209220
},
210221
() => <EmptyStateNoMessagesInWorkspace />
211222
)
223+
.with(
224+
{
225+
hasWorkspaceMessages: true,
226+
hasMultipleWorkspaces: P.any,
227+
view: AlertsFilterView.PII,
228+
isLoading: false,
229+
},
230+
() => <EmptyStatePII />
231+
)
212232
.with(
213233
{
214234
hasWorkspaceMessages: true,

src/features/dashboard-messages/components/table-messages.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useClientSidePagination } from '@/hooks/useClientSidePagination'
1616
import { TableAlertTokenUsage } from './table-alert-token-usage'
1717

1818
import { useMessagesFilterSearchParams } from '../hooks/use-messages-filter-search-params'
19-
import { Key01, PackageX } from '@untitled-ui/icons-react'
19+
import { Key01, PackageX, Passport } from '@untitled-ui/icons-react'
2020
import {
2121
EmptyStateError,
2222
TableMessagesEmptyState,
@@ -31,6 +31,7 @@ import {
3131
TableMessagesColumn,
3232
} from '../constants/table-messages-columns'
3333
import { formatTime } from '@/lib/format-time'
34+
import { isAlertPii } from '@/lib/is-alert-pii'
3435

3536
const getPromptText = (conversation: Conversation) => {
3637
return (conversation.question_answers[0]?.question?.message ?? 'N/A')
@@ -52,10 +53,12 @@ function getTypeText(type: QuestionType) {
5253
function countAlerts(alerts: Alert[]): {
5354
secrets: number
5455
malicious: number
56+
pii: number
5557
} {
5658
return {
5759
secrets: alerts.filter(isAlertSecret).length,
5860
malicious: alerts.filter(isAlertMalicious).length,
61+
pii: alerts.filter(isAlertPii).length,
5962
}
6063
}
6164

@@ -93,7 +96,7 @@ function AlertsSummaryCount({
9396
}
9497

9598
function AlertsSummaryCellContent({ alerts }: { alerts: Alert[] }) {
96-
const { malicious, secrets } = countAlerts(alerts)
99+
const { malicious, secrets, pii } = countAlerts(alerts)
97100

98101
return (
99102
<div className="flex items-center gap-2">
@@ -113,6 +116,14 @@ function AlertsSummaryCellContent({ alerts }: { alerts: Alert[] }) {
113116
count={secrets}
114117
icon={Key01}
115118
/>
119+
<AlertsSummaryCount
120+
strings={{
121+
singular: 'personally identifiable information (PII)',
122+
plural: 'personally identifiable information (PII)',
123+
}}
124+
count={pii}
125+
icon={Passport}
126+
/>
116127
</div>
117128
)
118129
}

src/features/dashboard-messages/components/tabs-messages.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ import {
1818
import { SearchFieldMessages } from './search-field-messages'
1919
import { tv } from 'tailwind-variants'
2020
import { useQueryGetWorkspaceMessages } from '@/hooks/use-query-get-workspace-messages'
21+
import { isConversationWithPII } from '@/lib/is-alert-pii'
2122

2223
type AlertsCount = {
2324
all: number
2425
malicious: number
2526
secrets: number
27+
pii: number
2628
}
2729

2830
function select(data: V1GetWorkspaceMessagesResponse): AlertsCount {
@@ -36,10 +38,13 @@ function select(data: V1GetWorkspaceMessagesResponse): AlertsCount {
3638
isConversationWithSecretAlerts,
3739
]).length
3840

41+
const pii: number = multiFilter(data, [isConversationWithPII]).length
42+
3943
return {
4044
all,
4145
malicious,
4246
secrets,
47+
pii,
4348
}
4449
}
4550

@@ -103,6 +108,7 @@ export function TabsMessages({ children }: { children: React.ReactNode }) {
103108
count={data?.secrets ?? 0}
104109
id={AlertsFilterView.SECRETS}
105110
/>
111+
<Tab title="PII" count={data?.pii ?? 0} id={AlertsFilterView.PII} />
106112
</TabList>
107113

108114
<SearchFieldMessages className="ml-auto" />

src/features/dashboard-messages/hooks/use-messages-filter-search-params.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export enum AlertsFilterView {
66
ALL = 'all',
77
MALICIOUS = 'malicious',
88
SECRETS = 'secrets',
9+
PII = 'pii',
910
}
1011

1112
const alertsFilterSchema = z.object({

src/features/dashboard-messages/hooks/use-query-get-workspace-messages-table.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { isConversationWithMaliciousAlerts } from '../../../lib/is-alert-malicio
99
import { isConversationWithSecretAlerts } from '../../../lib/is-alert-secret'
1010
import { filterMessagesBySubstring } from '../lib/filter-messages-by-substring'
1111
import { useQueryGetWorkspaceMessages } from '@/hooks/use-query-get-workspace-messages'
12+
import { isConversationWithPII } from '@/lib/is-alert-pii'
1213

1314
const FILTER: Record<
1415
AlertsFilterView,
@@ -17,6 +18,7 @@ const FILTER: Record<
1718
all: () => true,
1819
malicious: isConversationWithMaliciousAlerts,
1920
secrets: isConversationWithSecretAlerts,
21+
pii: isConversationWithPII,
2022
}
2123

2224
export function useQueryGetWorkspaceMessagesTable() {

0 commit comments

Comments
 (0)