store/cockpitApi: query for oscap customizations

Use the oscap & scap-security guide packages on the host to get the
customizations for an OpenSCAP profile item.
This commit is contained in:
Gianluca Zuccarelli 2025-03-10 10:07:35 +00:00 committed by Lucas Garfield
parent 12a2649198
commit c7cd9e8de3
5 changed files with 93 additions and 10 deletions

View file

@ -24,7 +24,11 @@ import {
RHEL_10_BETA,
RHEL_10,
} from '../../../../constants';
import { useGetOscapProfilesQuery } from '../../../../store/backendApi';
import {
useGetOscapProfilesQuery,
useGetOscapCustomizationsQuery,
useLazyGetOscapCustomizationsQuery,
} from '../../../../store/backendApi';
import { usePoliciesQuery, PolicyRead } from '../../../../store/complianceApi';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
@ -32,8 +36,6 @@ import {
DistributionProfileItem,
Filesystem,
OpenScapProfile,
useGetOscapCustomizationsQuery,
useLazyGetOscapCustomizationsQuery,
Services,
} from '../../../../store/imageBuilderApi';
import {

View file

@ -12,12 +12,10 @@ import {
} from '@patternfly/react-core';
import { RELEASES } from '../../../../constants';
import { useGetOscapCustomizationsQuery } from '../../../../store/backendApi';
import { PolicyRead, usePolicyQuery } from '../../../../store/complianceApi';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
OpenScapProfile,
useGetOscapCustomizationsQuery,
} from '../../../../store/imageBuilderApi';
import { OpenScapProfile } from '../../../../store/imageBuilderApi';
import {
changeCompliance,
selectCompliancePolicyID,

View file

@ -16,9 +16,11 @@ import {
COMPLIANCE_AND_VULN_SCANNING_URL,
COMPLIANCE_URL,
} from '../../../../constants';
import { useBackendPrefetch } from '../../../../store/backendApi';
import {
useBackendPrefetch,
useGetOscapCustomizationsQuery,
} from '../../../../store/backendApi';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import { useGetOscapCustomizationsQuery } from '../../../../store/imageBuilderApi';
import {
ComplianceType,
selectComplianceProfileID,

View file

@ -35,6 +35,14 @@ export const useGetOscapProfilesQuery = process.env.IS_ON_PREMISE
? cockpitQueries.useGetOscapProfilesQuery
: serviceQueries.useGetOscapProfilesQuery;
export const useGetOscapCustomizationsQuery = process.env.IS_ON_PREMISE
? cockpitQueries.useGetOscapCustomizationsQuery
: serviceQueries.useGetOscapCustomizationsQuery;
export const useLazyGetOscapCustomizationsQuery = process.env.IS_ON_PREMISE
? cockpitQueries.useLazyGetOscapCustomizationsQuery
: serviceQueries.useLazyGetOscapCustomizationsQuery;
export const useComposeBlueprintMutation = process.env.IS_ON_PREMISE
? cockpitQueries.useComposeBlueprintMutation
: serviceQueries.useComposeBlueprintMutation;

View file

@ -8,6 +8,7 @@ import path from 'path';
// We also needed to create an alias in vitest to make this work.
import cockpit from 'cockpit';
import { fsinfo } from 'cockpit/fsinfo';
import TOML from 'toml';
import { v4 as uuidv4 } from 'uuid';
// We have to work around RTK query here, since it doesn't like splitting
@ -18,7 +19,10 @@ import { v4 as uuidv4 } from 'uuid';
// bit so that the `cockpitApi` doesn't become a monolith.
import { contentSourcesApi } from './contentSourcesApi';
import { mapHostedToOnPrem } from '../../Components/Blueprints/helpers/onPremToHostedBlueprintMapper';
import {
mapHostedToOnPrem,
mapOnPremToHosted,
} from '../../Components/Blueprints/helpers/onPremToHostedBlueprintMapper';
import { BLUEPRINTS_DIR } from '../../constants';
import {
ComposeBlueprintApiResponse,
@ -48,6 +52,8 @@ import {
UpdateBlueprintApiResponse,
UpdateBlueprintApiArg,
DistributionProfileItem,
GetOscapCustomizationsApiResponse,
GetOscapCustomizationsApiArg,
} from '../service/imageBuilderApi';
const lookupDatastreamDistro = (distribution: string) => {
@ -347,6 +353,71 @@ export const cockpitApi = contentSourcesApi.injectEndpoints({
}
},
}),
getOscapCustomizations: builder.query<
GetOscapCustomizationsApiResponse,
GetOscapCustomizationsApiArg
>({
queryFn: async ({ distribution, profile }) => {
try {
const dsDistro = lookupDatastreamDistro(distribution);
let result = (await cockpit.spawn(
[
'oscap',
'xccdf',
'generate',
'fix',
'--fix-type',
'blueprint',
'--profile',
profile,
`/usr/share/xml/scap/ssg/content/ssg-${dsDistro}-ds.xml`,
],
{
superuser: 'try',
}
)) as string;
const parsed = TOML.parse(result);
const blueprint = mapOnPremToHosted(parsed);
result = (await cockpit.spawn(
[
'oscap',
'info',
'--profile',
profile,
`/usr/share/xml/scap/ssg/content/ssg-${dsDistro}-ds.xml`,
],
{
superuser: 'try',
}
)) as string;
const descriptionLine = result
.split('\n')
.filter((s) => s.includes('Description: '));
const description =
descriptionLine.length > 0
? descriptionLine[0].split('Description: ')[1]
: '';
return {
data: {
...blueprint.customizations,
openscap: {
profile_id: profile,
// the profile name is stored in the description
profile_name: blueprint.description,
profile_description: description,
},
},
};
} catch (error) {
return { error };
}
},
}),
composeBlueprint: builder.mutation<
ComposeBlueprintApiResponse,
ComposeBlueprintApiArg
@ -526,6 +597,8 @@ export const {
useUpdateBlueprintMutation,
useDeleteBlueprintMutation,
useGetOscapProfilesQuery,
useGetOscapCustomizationsQuery,
useLazyGetOscapCustomizationsQuery,
useComposeBlueprintMutation,
useGetComposesQuery,
useGetBlueprintComposesQuery,