Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Add New Campaign - Source Dropdown #183

Merged
merged 5 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 78 additions & 50 deletions applications/client/src/views/Campaigns/Upload/NewCampaignDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
import { Tab } from '@blueprintjs/core';
import type { OptionProps } from '@blueprintjs/core';
import { Divider, FormGroup, HTMLSelect } from '@blueprintjs/core';
import { css } from '@emotion/react';
import { DialogEx, ErrorFallback } from '@redeye/client/components';
import { RedEyeDbUploadForm } from '@redeye/client/views';
import { ExternalLink, Header, TabsStyled, Txt, UtilityStyles } from '@redeye/ui-styles';
import { CoreTokens, ExternalLink, Header, Txt } from '@redeye/ui-styles';
import { observer } from 'mobx-react-lite';
import type { ComponentProps } from 'react';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import type { ParserInfoModel } from '../../../store';
import { useStore } from '../../../store';
import { ParserUploadForm } from './ParserUploadForm';

enum CampaignTabs {
NEW,
UPLOAD = 9999,
}

type NewCampaignDialogProps = ComponentProps<'div'> & {
open: boolean;
onClose: (...args: any) => void;
};

const SOURCE_UNSET = '';

export const NewCampaignDialog = observer<NewCampaignDialogProps>(({ ...props }) => {
const store = useStore();
const [currentTab, setCurrentTab] = useState(store.appMeta.blueTeam ? CampaignTabs.UPLOAD : (0 as CampaignTabs));
const [currentUploadOptionValue, setCurrentUploadOptionValue] = useState(SOURCE_UNSET);

const uploadOptions = useMemo(() => {
const options: (OptionProps & {
parserInfo?: ParserInfoModel;
})[] = [
{
label: 'Select Source',
value: SOURCE_UNSET,
disabled: true,
},
];

options.push(
...Array.from(store.graphqlStore.parserInfos.values())
.sort((a) => (a.name.includes('Cobalt') ? -1 : 1))
.map((parserInfo) => ({
label: parserInfo?.uploadForm?.tabTitle,
value: parserInfo.id,
parserInfo,
}))
);

options.push({
label: '.redeye file',
value: 'redeye-file',
});

return options;
}, [store.graphqlStore.parserInfos.values()]);

const selectedUploadOption = useMemo(
() => uploadOptions.find((option) => option.value === currentUploadOptionValue),
[uploadOptions, currentUploadOptionValue]
);

return (
<DialogEx
Expand All @@ -37,52 +70,47 @@ export const NewCampaignDialog = observer<NewCampaignDialogProps>(({ ...props })
<Header large css={{ margin: '2rem 1.5rem 1rem' }}>
Add a Campaign
</Header>
<TabsStyled
selectedTabId={currentTab}
onChange={(newTab) => setCurrentTab(newTab as CampaignTabs)}
id="add-campaign-methods"
renderActiveTabPanelOnly
>
{Array.from(store.graphqlStore.parserInfos.values())
.sort((a) => (a.name.includes('Cobalt') ? -1 : 1))
.map((parserInfo, index) => (
<Tab
cy-test={`create-new-camp-${parserInfo.id}`}
id={index}
key={parserInfo.id}
title={parserInfo?.uploadForm?.tabTitle}
panel={
!parserInfo?.uploadForm?.enabledInBlueTeam && store.appMeta.blueTeam ? (
<BlueTeamSourceWarning css={shadowStyle} />
) : (
<ParserUploadForm parserInfo={parserInfo} onClose={props.onClose} css={shadowStyle} />
)
}
/>
))}
<Tab
cy-test="upload-from-file"
id={CampaignTabs.UPLOAD}
title="Upload .redeye file"
panel={<RedEyeDbUploadForm onClose={props.onClose} css={shadowStyle} />}
<FormGroup css={{ padding: '1rem 1.5rem 1.5rem 1.5rem', margin: 0 }} label="Source">
<HTMLSelect
cy-test="create-new-camp" // <- was {`create-new-camp-${parserInfo.id}`}
value={currentUploadOptionValue}
css={!currentUploadOptionValue && htmlSelectPlaceholderStyle}
options={uploadOptions}
onChange={(e) => setCurrentUploadOptionValue(e.target.value)}
fill
large
/>
</TabsStyled>
</FormGroup>
<Divider css={{ margin: '0 1.5rem' }} />
{currentUploadOptionValue === SOURCE_UNSET ? (
<div css={{ padding: '1.5rem' }}>
<Txt italic muted>
Select an import source to continue
</Txt>
</div>
) : selectedUploadOption?.parserInfo == null ? (
<RedEyeDbUploadForm onClose={props.onClose} />
) : !selectedUploadOption?.parserInfo?.uploadForm?.enabledInBlueTeam && store.appMeta.blueTeam ? (
<div css={{ padding: '1.5rem' }}>
<Txt cy-test="bt-warning" running>
This upload source is not available in BlueTeam mode.
<br />
<ExternalLink href="https://github.com/cisagov/redeye#red-team--blue-team-modes">
Learn more
</ExternalLink>
</Txt>
</div>
) : (
<ParserUploadForm parserInfo={selectedUploadOption?.parserInfo} onClose={props.onClose} />
)}
</div>
</ErrorBoundary>
</DialogEx>
);
});

const shadowStyle = css`
${UtilityStyles.innerBoxShadowOverlay('top', 3, false)}
const htmlSelectPlaceholderStyle = css`
select {
color: ${CoreTokens.TextDisabled};
}
`;

const BlueTeamSourceWarning = (props) => (
<div css={{ padding: 24 }} {...props}>
<Txt cy-test="bt-warning" running>
This upload source is not available in BlueTeam mode.
<br />
<ExternalLink href="https://github.com/cisagov/redeye#red-team--blue-team-modes">Learn more</ExternalLink>
</Txt>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const RedEyeDbUploadForm = observer<RedEyeDbUploadFormProps>(({ ...props
inputProps={inputProps}
text={state.fileData ? state.fileName : 'No file selected'}
large
fill
/>
</FormGroup>
</DialogBodyEx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ describe('Verify Blue Team Version', () => {
it('Should Only See Upload from File', () => {
cy.get('[cy-test=add-campaign-btn]').click();

cy.get('[cy-test=upload-from-file]').should('be.visible');

cy.get('[cy-test=create-new-camp-cobalt-strike-parser]').click();
cy.get('[cy-test=create-new-camp]').select(1);

cy.get('[cy-test=bt-warning]')
.should('be.visible')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('Duplicate Campaign', () => {

// Upload another campaign with the same name
cy.get('[cy-test=add-campaign-btn]').click();
cy.get('[cy-test=upload-from-file]').click();
cy.get('[cy-test=create-new-camp]').select(3);
cy.get('[cy-test=new-camp-name]').click().type(camp);
cy.fixture(fileName, { encoding: null }).as('myFixture');
cy.get('[cy-test=browse-for-file]').selectFile('@myFixture');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('Upload raw log', () => {
it('Upload raw log and verify counts', () => {
cy.get('[cy-test=add-campaign-btn]').click();

cy.get('[cy-test=create-new-camp-cobalt-strike-parser]').click();
cy.get('[cy-test=create-new-camp]').select(1);

cy.uploadLogs('seb', camp, 'cobalt-strike-parser');

Expand Down
4 changes: 2 additions & 2 deletions applications/redeye-e2e/src/support/campaignCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Cypress.Commands.add('clickAboutOnCampaignCard', () => {
//UPLOAD CAMPAIGN DB FILE
Cypress.Commands.add('uploadCampaign', (camp, fileName) => {
cy.get('[cy-test=add-campaign-btn]').click();
cy.get('[cy-test=upload-from-file]').click();
cy.get('[cy-test=create-new-camp]').select(3);
cy.get('[cy-test=new-camp-name]').click().type(camp);
cy.fixture(fileName, { encoding: null }).as('myFixture');
cy.get('[cy-test=browse-for-file]').selectFile('@myFixture');
Expand All @@ -58,7 +58,7 @@ Cypress.Commands.add('uploadFolder', (camp, fileName) => {
//UPLOAD CAMPAIGN DB FILE
Cypress.Commands.add('uploadCampaignBlue', (camp, fileName) => {
cy.get('[cy-test=add-campaign-btn]').click();
cy.get('[cy-test=upload-from-file]').click();
cy.get('[cy-test=create-new-camp]').select(3);
cy.get('[cy-test=new-camp-name]').click().type(camp);
cy.fixture(fileName, { encoding: null }).as('myFixture');
cy.get('[cy-test=browse-for-file]').selectFile('@myFixture');
Expand Down
6 changes: 3 additions & 3 deletions docs/parser-guide/Create Parser Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ function info() {

// Configures the upload form in the RedEye client for the parser
uploadForm: {
serverDelineation: 'Database', // How our C2 data is seperated, by folders of files or database/config file
serverDelineation: 'Database', // How our C2 data is separated, by folders of files or database/config file
enabledInBlueTeam: false, // Whether the parser is enabled for Blue Team users
tabTitle: 'Upload C2 Log Files', // The title of the upload tab
tabTitle: 'C2 Framework', // The title of the upload option in the selection menu
fileUpload: {
type: 'Directory', // The type of upload, is it a single file or a directory
validate: 'None', // How to validate the files uploaded, more on this later
Expand All @@ -82,7 +82,7 @@ if (process.argv[2] === 'info') {
}
```

If you put this code in a file (for the example, we called our file `custom-redeye-parser`), make it executable, and place it in `parsers` folder; You can start RedEye (with "parsers" set to true RedEye's `config.json`) and see our parser available in the upload tab.
If you put this code in a file (for the example, we called our file `custom-redeye-parser`), make it executable, and place it in `parsers` folder; You can start RedEye (with "parsers" set to true RedEye's `config.json`) and see our parser available in the "Add Campaign" dialog's Source options.

![Upload Tab](./custom-parser-upload-tab.png)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ An object that configures the upload form in RedEye's UI
```ts
// upload a directory of files that are organized by server name and date in the format: <FOLDER_TO_UPLOAD>/<SERVER_NAME>/<YYYYMMDD>/
uploadForm = {
tabTitle: 'Upload <C2_NAME> logs',
tabTitle: '<C2_NAME>',
enabledInBlueTeam: false,
serverDelineation: ServerDelineationTypes.Folder,
fileUpload: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ The title of the tab in the upload form
**`Example`**

```ts
tabTitle = 'Upload <C2_NAME> logs';
tabTitle = '<C2_NAME>';
```

#### Defined in
Expand Down
Binary file modified docs/parser-guide/custom-parser-upload-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion parsers/brute-ratel-parser/src/info.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const aboutInfo: ParserInfo = {
uploadForm: {
serverDelineation: ServerDelineationTypes.Database,
enabledInBlueTeam: false,
tabTitle: 'Upload Brute Ratel logs',
tabTitle: 'Brute Ratel',
fileUpload: {
type: UploadType.Directory,
validate: ValidationMode.Parser,
Expand Down
2 changes: 1 addition & 1 deletion parsers/cobalt-strike-parser/src/info.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const aboutInfo: ParserInfo = {
uploadForm: {
serverDelineation: ServerDelineationTypes.Folder,
enabledInBlueTeam: false,
tabTitle: 'Upload Cobalt Strike logs',
tabTitle: 'Cobalt Strike',
fileUpload: {
type: UploadType.Directory,
validate: ValidationMode.Parser,
Expand Down
2 changes: 1 addition & 1 deletion parsers/parser-core/src/parser-info/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface ParserInfo {
* @example
* // upload a directory of files that are organized by server name and date in the format: <FOLDER_TO_UPLOAD>/<SERVER_NAME>/<YYYYMMDD>/
* uploadForm = {
* tabTitle: 'Upload <C2_NAME> logs',
* tabTitle: '<C2_NAME>',
* enabledInBlueTeam: false,
* serverDelineation: ServerDelineationTypes.Folder,
* fileUpload: {
Expand Down
2 changes: 1 addition & 1 deletion parsers/parser-core/src/parser-info/upload-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface UploadForm {
/**
* The title of the tab in the upload form
* @example
* tabTitle = 'Upload <C2_NAME> logs'
* tabTitle = '<C2_NAME>'
*/
tabTitle: string;
/**
Expand Down
Loading