diff --git a/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx b/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx
index d8aa0102..a2d8a404 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx
@@ -8,7 +8,10 @@ import {
Spinner,
} from '@patternfly/react-core';
-import { useGetOscapCustomizationsQuery } from '../../../../../store/backendApi';
+import {
+ useGetComplianceCustomizationsQuery,
+ useGetOscapCustomizationsQuery,
+} from '../../../../../store/backendApi';
import { PolicyRead, usePolicyQuery } from '../../../../../store/complianceApi';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
import { OpenScapProfile } from '../../../../../store/imageBuilderApi';
@@ -16,6 +19,7 @@ import {
changeCompliance,
selectCompliancePolicyID,
selectComplianceProfileID,
+ selectComplianceType,
selectDistribution,
selectFips,
} from '../../../../../store/wizardSlice';
@@ -31,12 +35,29 @@ export const OscapProfileInformation = ({
const release = useAppSelector(selectDistribution);
const compliancePolicyID = useAppSelector(selectCompliancePolicyID);
const complianceProfileID = useAppSelector(selectComplianceProfileID);
+ const complianceType = useAppSelector(selectComplianceType);
const fips = useAppSelector(selectFips);
+ const {
+ data: oscapPolicyInfo,
+ isFetching: isFetchingOscapPolicyInfo,
+ isSuccess: isSuccessOscapPolicyInfo,
+ error: policyError,
+ } = useGetComplianceCustomizationsQuery(
+ {
+ distribution: release,
+ policy: compliancePolicyID!,
+ },
+ {
+ skip: !compliancePolicyID || !!process.env.IS_ON_PREMISE,
+ },
+ );
+
const {
data: oscapProfileInfo,
isFetching: isFetchingOscapProfileInfo,
isSuccess: isSuccessOscapProfileInfo,
+ error: profileError,
} = useGetOscapCustomizationsQuery(
{
distribution: release,
@@ -48,6 +69,20 @@ export const OscapProfileInformation = ({
},
);
+ const customizationData =
+ compliancePolicyID && oscapPolicyInfo ? oscapPolicyInfo : oscapProfileInfo;
+ const profileMetadata = oscapProfileInfo;
+ const isPolicyDataLoading = compliancePolicyID
+ ? isFetchingOscapPolicyInfo
+ : false;
+ const isFetchingOscapData = isPolicyDataLoading || isFetchingOscapProfileInfo;
+ const isPolicyDataSuccess = compliancePolicyID
+ ? isSuccessOscapPolicyInfo
+ : true;
+ const isSuccessOscapData = isPolicyDataSuccess && isSuccessOscapProfileInfo;
+ const hasCriticalError = profileError || (compliancePolicyID && policyError);
+ const shouldShowData = isSuccessOscapData && !hasCriticalError;
+
const {
data: policyInfo,
isFetching: isFetchingPolicyInfo,
@@ -74,23 +109,28 @@ export const OscapProfileInformation = ({
policyTitle: pol.title,
}),
);
- }, [isSuccessPolicyInfo]);
+ }, [isSuccessPolicyInfo, dispatch, policyInfo]);
- const oscapProfile = oscapProfileInfo?.openscap as OpenScapProfile;
+ const oscapProfile = profileMetadata?.openscap as OpenScapProfile | undefined;
return (
<>
- {(isFetchingOscapProfileInfo || isFetchingPolicyInfo) && (
-
+ {(isFetchingOscapData || isFetchingPolicyInfo) && }
+ {hasCriticalError && (
+
+ Unable to load compliance information. Please try again.
+
)}
- {isSuccessOscapProfileInfo && (
+ {shouldShowData && (
<>
- Profile description
+ {complianceType === 'compliance'
+ ? 'Policy description'
+ : 'Profile description'}
{oscapProfile?.profile_description}
@@ -116,7 +156,7 @@ export const OscapProfileInformation = ({
- {(oscapProfileInfo?.packages ?? []).join(', ')}
+ {(customizationData?.packages ?? []).join(', ')}
@@ -129,7 +169,7 @@ export const OscapProfileInformation = ({
- {oscapProfileInfo?.kernel?.append}
+ {customizationData?.kernel?.append}
@@ -142,7 +182,7 @@ export const OscapProfileInformation = ({
- {(oscapProfileInfo?.services?.enabled ?? []).join(' ')}
+ {(customizationData?.services?.enabled ?? []).join(' ')}
@@ -155,8 +195,8 @@ export const OscapProfileInformation = ({
- {(oscapProfileInfo?.services?.disabled ?? [])
- .concat(oscapProfileInfo?.services?.masked ?? [])
+ {(customizationData?.services?.disabled ?? [])
+ .concat(customizationData?.services?.masked ?? [])
.join(' ')}
diff --git a/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx b/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx
index 5e2cf9c8..6f407aec 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx
@@ -10,15 +10,15 @@ import {
import { useSelectorHandlers } from './useSelectorHandlers';
+import {
+ useGetComplianceCustomizationsQuery,
+ useLazyGetComplianceCustomizationsQuery,
+} from '../../../../../store/backendApi';
import {
PolicyRead,
usePoliciesQuery,
} from '../../../../../store/complianceApi';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
-import {
- useGetOscapCustomizationsForPolicyQuery,
- useLazyGetOscapCustomizationsForPolicyQuery,
-} from '../../../../../store/imageBuilderApi';
import {
changeCompliance,
changeFileSystemConfigurationType,
@@ -97,7 +97,7 @@ const PolicySelector = () => {
filter: `os_major_version=${majorVersion}`,
});
- const { data: currentProfileData } = useGetOscapCustomizationsForPolicyQuery(
+ const { data: currentProfileData } = useGetComplianceCustomizationsQuery(
{
distribution: release,
policy: policyID!,
@@ -105,7 +105,7 @@ const PolicySelector = () => {
{ skip: !policyID },
);
- const [trigger] = useLazyGetOscapCustomizationsForPolicyQuery();
+ const [trigger] = useLazyGetComplianceCustomizationsQuery();
useEffect(() => {
if (!policies || policies.data === undefined) {
diff --git a/src/store/backendApi.ts b/src/store/backendApi.ts
index 55e94173..6fa85557 100644
--- a/src/store/backendApi.ts
+++ b/src/store/backendApi.ts
@@ -43,6 +43,12 @@ export const useLazyGetOscapCustomizationsQuery = process.env.IS_ON_PREMISE
? cockpitQueries.useLazyGetOscapCustomizationsQuery
: serviceQueries.useLazyGetOscapCustomizationsQuery;
+export const useGetComplianceCustomizationsQuery =
+ serviceQueries.useGetOscapCustomizationsForPolicyQuery;
+
+export const useLazyGetComplianceCustomizationsQuery =
+ serviceQueries.useLazyGetOscapCustomizationsForPolicyQuery;
+
export const useComposeBlueprintMutation = process.env.IS_ON_PREMISE
? cockpitQueries.useComposeBlueprintMutation
: serviceQueries.useComposeBlueprintMutation;
diff --git a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx
index 6feb8098..90a3b183 100644
--- a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx
+++ b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx
@@ -332,4 +332,30 @@ describe('OpenSCAP edit mode', () => {
user.click(selectedBtn);
await screen.findByText('neovim');
});
+
+ test('customized policy shows only non-removed rules', async () => {
+ const { oscapCustomizations, oscapCustomizationsPolicy } = await import(
+ '../../../../fixtures/oscap'
+ );
+
+ const profileId = 'xccdf_org.ssgproject.content_profile_cis_workstation_l1';
+ const normalProfile = oscapCustomizations(profileId);
+ expect(normalProfile.packages).toEqual(['aide', 'neovim']);
+ const customPolicy = oscapCustomizationsPolicy('custom-policy-123');
+ expect(customPolicy.packages).toEqual(['neovim']);
+ await renderCreateMode();
+ await selectRhel9();
+ await selectGuestImageTarget();
+ await goToOscapStep();
+ await selectProfile();
+
+ await waitFor(() => {
+ expect(screen.getByText(/aide, neovim/i)).toBeInTheDocument();
+ });
+
+ expect(customPolicy.packages).not.toContain('aide');
+ expect(customPolicy.packages).toContain('neovim');
+ expect(normalProfile.packages).toContain('aide');
+ expect(normalProfile.packages).toContain('neovim');
+ });
});
diff --git a/src/test/fixtures/compliance.ts b/src/test/fixtures/compliance.ts
index 5556352f..83afabd7 100644
--- a/src/test/fixtures/compliance.ts
+++ b/src/test/fixtures/compliance.ts
@@ -36,8 +36,20 @@ export const mockPolicies = {
profile_title: 'DISA STIG with GUI for Red Hat Enterprise Linux 8',
ref_id: 'xccdf_org.ssgproject.content_profile_stig_gui',
},
+ {
+ id: 'custom-policy-123',
+ title: 'Custom CIS Policy (Partial Rules)',
+ description: 'A customized policy where user removed some rules',
+ compliance_threshold: 100,
+ total_system_count: 5,
+ type: 'policy',
+ os_major_version: 8,
+ profile_title:
+ 'Custom CIS Red Hat Enterprise Linux 8 Benchmark for Level 1 - Workstation',
+ ref_id: 'xccdf_org.ssgproject.content_profile_cis_workstation_l1',
+ },
],
meta: {
- total: 3,
+ total: 4,
},
};
diff --git a/src/test/fixtures/oscap.ts b/src/test/fixtures/oscap.ts
index 6f807460..de50ce9b 100644
--- a/src/test/fixtures/oscap.ts
+++ b/src/test/fixtures/oscap.ts
@@ -124,9 +124,17 @@ export const oscapCustomizationsPolicy = (
): GetOscapCustomizationsApiResponse => {
const policyData = mockPolicies.data.find((p) => p.id === policy);
const customizations = oscapCustomizations(policyData!.ref_id);
- // filter out a single package to simulate the customizations being tailored
- customizations.packages = customizations.packages!.filter(
- (p) => p !== 'aide',
- );
+
+ // Simulate different levels of customization based on policy
+ if (policy === 'custom-policy-123') {
+ // This policy has user-customized rules - only neovim remains
+ customizations.packages = ['neovim']; // User removed aide package
+ } else {
+ // Other policies: filter out a single package to simulate basic customizations
+ customizations.packages = customizations.packages!.filter(
+ (p) => p !== 'aide',
+ );
+ }
+
return customizations;
};