Skip to content

Commit e1da5eb

Browse files
committed
Trim whitespace when splitting security-group annotations
The Governance Overview Standards/Categories/Controls cards, and the policy drill-down sidebar behind them, group policies by splitting the policy.open-cluster-management.io/{standards,categories,controls} annotation on ",". String.split(',') leaves a leading space on every token after the first (e.g. "A, B".split(',') -> ["A", " B"]), so the same logical value produces a second, distinct row whenever it isn't first in a comma-separated list on some policies but is standalone or first-in-list on others. Trim each token after splitting in both useSecurityGroupViolations (Overview.tsx) and the matching filter in SecurityGroupPolicySummarySidebar.tsx so aggregation and drill-down filtering key off the same normalized value regardless of annotation formatting. Reported in Red Hat case 04500068.
1 parent 4fa8714 commit e1da5eb

3 files changed

Lines changed: 75 additions & 2 deletions

File tree

frontend/src/routes/Governance/overview/Overview.test.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import GovernanceOverview from './Overview'
1717
import userEvent from '@testing-library/user-event'
1818
import { defaultContext, PluginDataContext } from '../../../lib/PluginDataContext'
19+
import { Policy, PolicyApiVersion, PolicyKind } from '../../../resources'
1920

2021
describe('Overview Page', () => {
2122
beforeEach(async () => nockIgnoreApiPaths())
@@ -141,4 +142,76 @@ describe('Overview Page', () => {
141142
userEvent.click(screen.getByText(/show 85 more/i))
142143
expect(queryByText(/show 85 more/i)).not.toBeInTheDocument()
143144
})
145+
146+
test('Should aggregate Standards card by trimmed annotation value, not raw comma-split token', async () => {
147+
// Regression test: a standard listed anywhere but first in a comma-separated
148+
// policy.open-cluster-management.io/standards annotation must not produce a
149+
// separate row on the Standards card just because of the leading space left
150+
// behind by String.split(',').
151+
const policyWithStandardFirstInList: Policy = {
152+
apiVersion: PolicyApiVersion,
153+
kind: PolicyKind,
154+
metadata: {
155+
name: 'policy-standards-first',
156+
namespace: 'test',
157+
uid: 'standards-test-uid-1',
158+
annotations: {
159+
'policy.open-cluster-management.io/standards': 'NIST SP 800-53, PCI-DSS 4.0',
160+
},
161+
},
162+
spec: {
163+
disabled: false,
164+
'policy-templates': [],
165+
remediationAction: 'inform',
166+
},
167+
status: {
168+
compliant: 'Compliant',
169+
},
170+
}
171+
const policyWithStandardLastInList: Policy = {
172+
apiVersion: PolicyApiVersion,
173+
kind: PolicyKind,
174+
metadata: {
175+
name: 'policy-standards-last',
176+
namespace: 'test',
177+
uid: 'standards-test-uid-2',
178+
annotations: {
179+
'policy.open-cluster-management.io/standards': 'CIS OpenShift Benchmark, PCI-DSS 4.0, NIST SP 800-53',
180+
},
181+
},
182+
spec: {
183+
disabled: false,
184+
'policy-templates': [],
185+
remediationAction: 'inform',
186+
},
187+
status: {
188+
compliant: 'Compliant',
189+
},
190+
}
191+
192+
const pluginData = {
193+
...defaultContext,
194+
loadStarted: true,
195+
loadCompleted: true,
196+
}
197+
render(
198+
<PluginDataContext.Provider value={pluginData}>
199+
<RecoilRoot
200+
initializeState={(snapshot) => {
201+
snapshot.set(policiesState, [policyWithStandardFirstInList, policyWithStandardLastInList])
202+
snapshot.set(managedClustersState, mockManagedClusters)
203+
}}
204+
>
205+
<MemoryRouter>
206+
<GovernanceOverview />
207+
</MemoryRouter>
208+
</RecoilRoot>
209+
</PluginDataContext.Provider>
210+
)
211+
212+
// Before the fix this rendered two separate rows/spans for the same logical
213+
// standard (one from the untrimmed " NIST SP 800-53" split token).
214+
expect(screen.getAllByText('NIST SP 800-53').length).toBe(1)
215+
expect(screen.getAllByText('PCI-DSS 4.0').length).toBe(1)
216+
})
144217
})

frontend/src/routes/Governance/overview/Overview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ function useSecurityGroupViolations(group: string, policies: Policy[]) {
100100
if (policy.spec.disabled) continue
101101
const annotation = policy.metadata.annotations?.[`policy.open-cluster-management.io/${group}`]
102102
if (!annotation) continue
103-
const names = annotation.split(',')
103+
const names = annotation.split(',').map((name) => name.trim())
104104
for (const name of names) {
105105
let v = clusterViolations[name]
106106
if (!v) {

frontend/src/routes/Governance/overview/SecurityGroupPolicySummarySidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function SecurityGroupPolicySummarySidebar(props: {
3636
if (!annotation) {
3737
return false
3838
}
39-
const names = annotation.split(',')
39+
const names = annotation.split(',').map((name) => name.trim())
4040
for (const name of names) {
4141
if (name === violation.name && policy.status?.compliant) {
4242
return true

0 commit comments

Comments
 (0)