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

Commit

Permalink
Merge pull request #107 from cisagov/BLDSTRIKE-450-mitre-attack-names
Browse files Browse the repository at this point in the history
Add MITRE ATT&CK names
  • Loading branch information
sang2925 authored Mar 27, 2023
2 parents d168c48 + 3fe1384 commit 3babd29
Show file tree
Hide file tree
Showing 9 changed files with 440,083 additions and 50 deletions.
29 changes: 29 additions & 0 deletions applications/client/src/components/Mitre/MitreAttack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { observer } from 'mobx-react-lite';
import type { ComponentProps } from 'react';
import { mitreAttackDictionary } from './mitreAttackDictionary';

type MitreAttackItem = {
name: string;
id: string;
url: string;
};
export type MitreAttackId = keyof typeof mitreAttackDictionary;
// type MitreAttackDictionary = Record<keyof typeof mitreAttackDictionary, MitreAttackItem>;

type MitreAttackProps = ComponentProps<'a'> & {
miterAttackId: keyof typeof mitreAttackDictionary;
};

export const MitreAttack = observer<MitreAttackProps>(({ miterAttackId, ...props }) => {
const { name, id, url } = mitreAttackDictionary[miterAttackId] as MitreAttackItem;
return (
<a
children={`${id}: ${name}`}
aria-label="Mitre attack links"
href={url}
target="_blank"
rel="noopener noreferrer"
{...props}
/>
);
});
435,694 changes: 435,694 additions & 0 deletions applications/client/src/components/Mitre/enterprise-attack.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions applications/client/src/components/Mitre/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './MitreAttack';
export * from './mitreAttackDictionary';
4,204 changes: 4,204 additions & 0 deletions applications/client/src/components/Mitre/mitreAttackDictionary.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Run this to generate a dataset of named MITRE ATT&CKs for use in the UI
* `node ./process-enterprise-attack.js`
* https://attack.mitre.org/
* https://github.com/mitre/cti
*/

const fs = require('fs');
const path = require('path');

// manually update this file from https://github.com/mitre/cti/blob/master/enterprise-attack/enterprise-attack.json
const enterpriseAttack = require('./enterprise-attack.json');
// we could also add the other items from ics, mobile, and pre? if they have the same format

const mitreAttackDictionary = {};

enterpriseAttack.objects.forEach((object) => {
const externalReference = object.external_references?.find((ref) => ref.source_name === 'mitre-attack');

// some of the externalReferences don't have a mitre-attack associated
if (!externalReference) return { name: object.name };

const { external_id: id, url } = externalReference;

const mitreAttack = {
name: object.name,
id,
url,
};

// Mire Attacks can have sub attacks formatted 'T0000.000'
const [parentTechnique, subTechnique] = id.split('.');

if (subTechnique != null) {
mitreAttack.parentTechnique = parentTechnique;

// add subTechnique to parentTechnique
if (mitreAttackDictionary[parentTechnique] == null) {
mitreAttackDictionary[parentTechnique] = { subTechniques: [subTechnique] };
} else if (mitreAttackDictionary[parentTechnique].subTechniques == null) {
mitreAttackDictionary[parentTechnique].subTechniques = [subTechnique];
} else {
mitreAttackDictionary[parentTechnique].subTechniques.push(subTechnique);
}
}

// mitreAttackDictionary[id] may have been added from the subTechnique process
if (mitreAttackDictionary[id] == null) {
mitreAttackDictionary[id] = mitreAttack;
} else {
mitreAttackDictionary[id] = {
...mitreAttackDictionary[id],
...mitreAttack,
};
}

return mitreAttack;
});

const alphabeticalMitreAttackDictionary = {};
Object.keys(mitreAttackDictionary)
.sort()
.forEach((id) => {
alphabeticalMitreAttackDictionary[id] = mitreAttackDictionary[id].subTechniques
? {
...mitreAttackDictionary[id],
subTechniques: mitreAttackDictionary[id].subTechniques.sort(),
}
: mitreAttackDictionary[id];
});

console.log(`Parsed ${Object.entries(alphabeticalMitreAttackDictionary).length} MITRE ATT&CK ids`);

// it helps to manually run prettier on this after its generated
const mitreAttackDictionaryPathTs = path.join(__dirname, 'mitreAttackDictionary.ts');
const tsFileContents = `export const mitreAttackDictionary = ${JSON.stringify(alphabeticalMitreAttackDictionary)}`;
fs.writeFile(mitreAttackDictionaryPathTs, tsFileContents, (err) => {
if (err) console.error(err);
});
17 changes: 17 additions & 0 deletions applications/client/src/components/Mitre/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@



# Download MITRE ATT&CK Framework Json

https://github.com/mitre/cti/blob/master/enterprise-attack/enterprise-attack.json




This data may need to be updated periodically
- Visit https://mitre-attack.github.io/attack-navigator/
- Select "Create New Layer" > "Enterprise"
- In the top toolbar under "Selection Controls" click "Search & MultiSelect" (the search icon 🔎)
- In the right panel, under "Techniques" click "Select All."
- With all techniques selected, In the top toolbar under "Layer Controls" click "Download layer as json" (the down arrow icon ⬇)
- replace the `layer.json` file with the newly downloaded one.
1 change: 1 addition & 0 deletions applications/client/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './utils';
export * from './Dialogs/index';
export * from './Forms/index';
export * from './Header/index';
export * from './Mitre/index';
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const Command = observer<CommandProps>(
/>
)}
<div className={skeletonClass} css={rowTextStyle}>
<Txt cy-test="command-header" ellipsize small muted block running title={command?.info.contextTooltipText}>
<Txt cy-test="command-header" ellipsize small muted block title={command?.info.contextTooltipText}>
<Txt cy-test="command-date-time" monospace>
{command?.info.time.format(`${dateShortFormat} ${timeFormat}`)}
</Txt>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Button, Intent } from '@blueprintjs/core';
import { ChevronSort16, Launch16 } from '@carbon/icons-react';
import { css } from '@emotion/react';
import { CarbonIcon } from '@redeye/client/components';
import type { MitreAttackId } from '@redeye/client/components';
import { CarbonIcon, MitreAttack } from '@redeye/client/components';
import { createState } from '@redeye/client/components/mobx-create-state';
import type { CommandModel } from '@redeye/client/store';
import { useStore } from '@redeye/client/store';
import { ScreenShotCommand } from '@redeye/client/views';
import { FlexSplitter, Txt, CoreTokens, UtilityStyles } from '@redeye/ui-styles';
import { Txt, CoreTokens, UtilityStyles, Flex, Spacer } from '@redeye/ui-styles';
import { observer } from 'mobx-react-lite';

type CommandOutputProps = {
Expand Down Expand Up @@ -50,38 +51,26 @@ export const CommandOutput = observer<CommandOutputProps>(({ command }) => {
No MITRE ATT&amp;CKs
</Txt>
) : (
command?.uniqueAttackIds?.map((mitreAttack) => (
<a
cy-test="mitre-attack-link"
aria-label="Mitre attack links"
key={mitreAttack}
href={`https://attack.mitre.org/${
mitreAttack.includes('TA') ? 'tactics' : 'techniques'
}/${mitreAttack.replace(/\./g, '/')}/`}
target="_blank"
rel="noopener noreferrer"
css={css`
margin-right: 0.5rem;
display: inline-block; // handle wrapping (for lots of attackIds)
`}
children={mitreAttack}
/>
command?.uniqueAttackIds?.map((mitreAttack, i) => (
<>
{i === 0 ? (
<Txt small>
MITRE ATT&amp;CKs:
<Spacer />
</Txt>
) : (
<Spacer>·</Spacer>
)}
<MitreAttack
miterAttackId={mitreAttack as MitreAttackId}
cy-test="mitre-attack-link"
key={mitreAttack}
css={{ display: 'inline-block' }}
/>
</>
))
)}
</div>
<FlexSplitter />
{command && (
<Button
cy-test="openRawLogs"
text="Show in Raw Logs"
rightIcon={<CarbonIcon icon={Launch16} />}
intent={Intent.PRIMARY}
onClick={() => {
store.router.updateQueryParams({ queryParams: { 'raw-command': command.id } });
}}
minimal
/>
)}
</div>
<div css={outputScrollWrapperStyle} cy-test="log-details">
<div css={outputOverflowWrapperStyle}>
Expand All @@ -97,24 +86,39 @@ export const CommandOutput = observer<CommandOutputProps>(({ command }) => {
{state.renderedLines?.slice(2, state.renderedLines.length).join('\n')}
{command?.inputText === 'screenshot' && <ScreenShotCommand command={command} />}
</pre>
{state.collapsedLineCount > 0 && (
<div css={showMoreWrapperStyle}>
<Flex align="center" css={showMoreWrapperStyle}>
{state.collapsedLineCount > 0 && (
<>
<Button
cy-test="showMoreLines"
icon={<CarbonIcon icon={ChevronSort16} />}
onClick={state.toggleShowAll}
text={
state.showAll
? 'Show less'
: `Show ${state.collapsedLineCount} more line${state.collapsedLineCount === 1 ? '' : 's'}`
}
intent={Intent.PRIMARY}
minimal
small
/>
<Spacer>|</Spacer>
</>
)}
{command && (
<Button
cy-test="showMoreLines"
icon={<CarbonIcon icon={ChevronSort16} />}
cy-test="openRawLogs"
text="Show Raw Logs"
rightIcon={<CarbonIcon icon={Launch16} />}
onClick={() => {
store.router.updateQueryParams({ queryParams: { 'raw-command': command.id } });
}}
intent={Intent.PRIMARY}
onClick={state.toggleShowAll}
css={showMoreButtonStyle}
minimal
small
text={
state.showAll
? 'Show less'
: `Show ${state.collapsedLineCount} more line${state.collapsedLineCount === 1 ? '' : 's'}`
}
/>
</div>
)}
)}
</Flex>
</>
) : (
<pre css={preStyles} cy-test="logInfo">
Expand All @@ -134,8 +138,8 @@ const outputMetaStyle = css`
width: 100%;
display: flex;
/* border-bottom: 1px solid ${CoreTokens.Background1}; */
align-items: baseline;
padding: 0 1rem 0 3rem;
/* align-items: baseline; */
padding: 0.25rem 1rem 0.25rem 3rem;
`;
const outputScrollWrapperStyle = css`
overflow-x: auto;
Expand All @@ -154,7 +158,10 @@ const preStyles = css`
`;
const showMoreWrapperStyle = css`
border-top: 1px solid ${CoreTokens.Background1};
padding: 0 1rem 0 2.25rem;
width: fit-content;
position: sticky;
left: 0;
`;
const showMoreButtonStyle = css`
margin: 0 1rem 0 2.25rem;
`;
// const showMoreButtonStyle = css`
// `;

0 comments on commit 3babd29

Please sign in to comment.