[ResponseOps][Cases] Disable md5 algo in cases files when fips mode enabled (#189074)

## Summary

Disables the `md5` hashing algorithm in cases files when Kibana is
running in FIPS mode.

## References

Closes #185600

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Umberto Pepato 2024-07-31 11:29:22 +02:00 committed by GitHub
parent 1e6e23a45d
commit b5f617c891
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 99 additions and 49 deletions

View file

@ -76,6 +76,36 @@ describe('FileActionsPopoverButton', () => {
expect(await screen.queryByTestId('cases-files-copy-hash-button')).not.toBeInTheDocument();
});
it('only renders menu items for the enabled hashes', async () => {
appMockRender.render(
<FileActionsPopoverButton
caseId={basicCaseId}
theFile={{ ...basicFileMock, hash: { sha1: 'sha1' } }}
/>
);
const popoverButton = await screen.findByTestId(
`cases-files-actions-popover-button-${basicFileMock.id}`
);
expect(popoverButton).toBeInTheDocument();
userEvent.click(popoverButton);
expect(
await screen.findByTestId(`cases-files-popover-${basicFileMock.id}`)
).toBeInTheDocument();
const copyFileHashButton = await screen.findByTestId('cases-files-copy-hash-button');
expect(copyFileHashButton).toBeInTheDocument();
userEvent.click(copyFileHashButton);
expect(await screen.findByTestId(`cases-files-copy-sha1-hash-button`)).toBeInTheDocument();
expect(screen.queryByTestId('cases-files-copy-md5-hash-button')).not.toBeInTheDocument();
expect(screen.queryByTestId('cases-files-copy-sha256-hash-button')).not.toBeInTheDocument();
});
it('clicking the copy file hash button rerenders the popover correctly', async () => {
appMockRender.render(<FileActionsPopoverButton caseId={basicCaseId} theFile={basicFileMock} />);

View file

@ -65,49 +65,52 @@ export const FileActionsPopoverButton: React.FC<{ caseId: string; theFile: FileJ
id: 1,
title: i18n.COPY_FILE_HASH,
items: [
{
name: 'MD5',
icon: 'copyClipboard',
disabled: !theFile.hash?.md5,
onClick: () => {
if (theFile.hash?.md5) {
navigator.clipboard.writeText(theFile.hash.md5).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('md5'));
});
theFile.hash?.md5
? {
name: 'MD5',
icon: 'copyClipboard',
onClick: () => {
if (theFile.hash?.md5) {
navigator.clipboard.writeText(theFile.hash.md5).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('md5'));
});
}
},
'data-test-subj': 'cases-files-copy-md5-hash-button',
}
},
'data-test-subj': 'cases-files-copy-md5-hash-button',
},
{
name: 'SHA1',
icon: 'copyClipboard',
disabled: !theFile.hash?.sha1,
onClick: () => {
if (theFile.hash?.sha1) {
navigator.clipboard.writeText(theFile.hash.sha1).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('sha1'));
});
: null,
theFile.hash?.sha1
? {
name: 'SHA1',
icon: 'copyClipboard',
onClick: () => {
if (theFile.hash?.sha1) {
navigator.clipboard.writeText(theFile.hash.sha1).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('sha1'));
});
}
},
'data-test-subj': 'cases-files-copy-sha1-hash-button',
}
},
'data-test-subj': 'cases-files-copy-sha1-hash-button',
},
{
name: 'SHA256',
icon: 'copyClipboard',
disabled: !theFile.hash?.sha256,
onClick: () => {
if (theFile.hash?.sha256) {
navigator.clipboard.writeText(theFile.hash.sha256).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('sha256'));
});
: null,
theFile.hash?.sha256
? {
name: 'SHA256',
icon: 'copyClipboard',
onClick: () => {
if (theFile.hash?.sha256) {
navigator.clipboard.writeText(theFile.hash.sha256).then(() => {
closePopover();
showSuccessToast(i18n.COPY_FILE_HASH_SUCCESS('sha256'));
});
}
},
'data-test-subj': 'cases-files-copy-sha256-hash-button',
}
},
'data-test-subj': 'cases-files-copy-sha256-hash-button',
},
],
: null,
].filter(Boolean) as EuiContextMenuPanelDescriptor['items'],
},
];

View file

@ -109,6 +109,15 @@ describe('server files', () => {
});
});
describe('hashing algorithms', () => {
it('excludes md5 when fips is enabled', () => {
const schema = ConfigSchema.validate({});
registerCaseFileKinds(schema.files, mockFilesSetup, true);
expect(mockFilesSetup.registerFileKind.mock.calls[0][0].hashes).not.toContain('md5');
});
});
describe('allowed mime types', () => {
describe('image png', () => {
const schema = ConfigSchema.validate({ files: { allowedMimeTypes: ['image/png'] } });

View file

@ -20,13 +20,17 @@ import { IMAGE_MIME_TYPES } from '../../common/constants/mime_types';
import type { FilesConfig } from './types';
import { constructFileKindIdByOwner, constructFilesHttpOperationTag } from '../../common/files';
const buildFileKind = (config: FilesConfig, owner: Owner): FileKind => {
const buildFileKind = (config: FilesConfig, owner: Owner, isFipsMode = false): FileKind => {
const hashes: FileKind['hashes'] = ['sha1', 'sha256'];
if (!isFipsMode) {
hashes.unshift('md5');
}
return {
id: constructFileKindIdByOwner(owner),
http: fileKindHttpTags(owner),
maxSizeBytes: createMaxCallback(config),
allowedMimeTypes: config.allowedMimeTypes,
hashes: ['md5', 'sha1', 'sha256'],
hashes,
};
};
@ -73,16 +77,20 @@ export const createMaxCallback =
/**
* The file kind definition for interacting with the file service for the backend
*/
const createFileKinds = (config: FilesConfig): Record<Owner, FileKind> => {
const createFileKinds = (config: FilesConfig, isFipsMode = false): Record<Owner, FileKind> => {
return {
[APP_ID]: buildFileKind(config, APP_ID),
[OBSERVABILITY_OWNER]: buildFileKind(config, OBSERVABILITY_OWNER),
[SECURITY_SOLUTION_OWNER]: buildFileKind(config, SECURITY_SOLUTION_OWNER),
[APP_ID]: buildFileKind(config, APP_ID, isFipsMode),
[OBSERVABILITY_OWNER]: buildFileKind(config, OBSERVABILITY_OWNER, isFipsMode),
[SECURITY_SOLUTION_OWNER]: buildFileKind(config, SECURITY_SOLUTION_OWNER, isFipsMode),
};
};
export const registerCaseFileKinds = (config: FilesConfig, filesSetupPlugin: FilesSetup) => {
const fileKinds = createFileKinds(config);
export const registerCaseFileKinds = (
config: FilesConfig,
filesSetupPlugin: FilesSetup,
isFipsMode = false
) => {
const fileKinds = createFileKinds(config, isFipsMode);
for (const fileKind of Object.values(fileKinds)) {
filesSetupPlugin.registerFileKind(fileKind);

View file

@ -86,7 +86,7 @@ export class CasePlugin
this.persistableStateAttachmentTypeRegistry
);
registerCaseFileKinds(this.caseConfig.files, plugins.files);
registerCaseFileKinds(this.caseConfig.files, plugins.files, core.security.fips.isEnabled());
this.securityPluginSetup = plugins.security;
this.lensEmbeddableFactory = plugins.lens.lensEmbeddableFactory;