Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDF2Splunk Updates #2705

Merged
merged 44 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f9647a1
Don't assume descriptions is defined
camdenmoors Feb 28, 2022
a6e7cb1
Add Splunk Logging
camdenmoors Feb 28, 2022
fb103e9
Add insecure mode
camdenmoors Feb 28, 2022
ea30ad1
Merge branch 'master' into splunkUpdates
camdenmoors Feb 28, 2022
2b67ec9
Upload multiple items to collector at once
camdenmoors Feb 28, 2022
771b849
Remove set index value, add hint for hostname field, fix profile subt…
camdenmoors Feb 28, 2022
ccfa6ad
Revert "Upload multiple items to collector at once"
camdenmoors Feb 28, 2022
95aea3e
Add hint for admin
camdenmoors Feb 28, 2022
17ae046
Remove spath.meta
camdenmoors Feb 28, 2022
4ea199a
Use index="*"
camdenmoors Feb 28, 2022
09152cb
Set empty array for Profile.controls
camdenmoors Feb 28, 2022
0162634
Log getExecution result
camdenmoors Feb 28, 2022
a7f3f38
Use replaceKeyValue descriptions
camdenmoors Feb 28, 2022
a572d18
Allow undefined return values, remove FileList log, increase timeout
camdenmoors Mar 1, 2022
6405e1d
Add better error handling to SplunkReader, remove admin hint
camdenmoors Mar 1, 2022
a65af6c
Post test data to webhook
camdenmoors Mar 1, 2022
abbc20f
Update test data, remove webhook
camdenmoors Mar 1, 2022
e19c397
Ignore LGTM issue and comment on insecure mode
camdenmoors Mar 1, 2022
6d00bf7
Allow passing custom search to SplunkReader
camdenmoors Mar 1, 2022
2d46faf
Use notify for success message
camdenmoors Mar 1, 2022
8c7ccf8
Revert "Allow passing custom search to SplunkReader"
camdenmoors Mar 1, 2022
925f6e4
Add title
camdenmoors Mar 2, 2022
4a8b235
Merge branch 'splunkUpdates' of https://github.com/mitre/heimdall2 in…
camdenmoors Mar 2, 2022
11053e2
Clean up SplunkReader usability, add splunk-sdk to hdf-converters, de…
camdenmoors Mar 2, 2022
dea1380
Switch to splunk-sdk for hdf2splunk, upload multiple events at once
camdenmoors Mar 3, 2022
ccfa477
Switch to @mitre/splunk-sdk-no-env
camdenmoors Mar 3, 2022
18fc56d
Follow Sonarqube suggestions, update mapper tests
camdenmoors Mar 4, 2022
8edb1a6
Update splunk_reverse_mapper.spec.ts
camdenmoors Mar 4, 2022
c1959dd
Don't redefine err, change debug to verbose
camdenmoors Mar 4, 2022
b1c42e4
Remove splunk-sdk from frontend
camdenmoors Mar 7, 2022
76f77b0
Post jobResponseData to webhook
camdenmoors Mar 7, 2022
91d37e1
Update upload index
camdenmoors Mar 7, 2022
af55281
Use HTTP scheme when posting data to splunk in CI
camdenmoors Mar 7, 2022
d98d136
Remove _raw parsing
camdenmoors Mar 7, 2022
bb4d76c
Use Event Breaker in CI
camdenmoors Mar 7, 2022
501eddb
Use volume without /opt/splunk
camdenmoors Mar 7, 2022
992066b
Handle Disabled Line Breaker in CI
camdenmoors Mar 7, 2022
80c9815
Remove double result parsing
camdenmoors Mar 7, 2022
cf23e0e
Specify Content-Type to webhook, log _raw
camdenmoors Mar 7, 2022
4fb36ee
Change event breaker newline to {{{FIX_EVENT_BREAKER_NEWLINE}}}
camdenmoors Mar 7, 2022
433dab8
Update event breaker for splunkData.controls
camdenmoors Mar 7, 2022
ab8ab42
Restore breaker type, remove expected data
camdenmoors Mar 7, 2022
144423a
Only ensure no errors on upload, comment on tests
camdenmoors Mar 7, 2022
91cd1a1
Remove waitForJob
camdenmoors Mar 7, 2022
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
28 changes: 17 additions & 11 deletions apps/frontend/src/components/global/upload_tabs/splunk/AuthStep.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@
v-model="hostname"
label="Hostname"
for="hostname_field"
hint="https://yourdomain.com:8089"
data-cy="splunkhostname"
/>
</v-form>
<v-row class="mx-1">
<v-btn
color="primary"
class="my-4 mt-0"
class="my-4 mt-4"
data-cy="splunkLoginButton"
@click="login"
>
Login
</v-btn>
<v-spacer />
<v-btn>
<v-btn @click="$emit('show-help')">
Help
<v-icon class="ml-2"> mdi-help-circle </v-icon>
</v-btn>
Expand Down Expand Up @@ -68,15 +69,20 @@ export default class AuthStep extends Vue {
this.username,
this.password
);
if (await splunkClient.validateCredentials()) {
localUsername.set(this.username);
localPassword.set(this.password);
localHostname.set(this.hostname);
SnackbarModule.failure('You have successfully signed in');
this.$emit('authenticated', splunkClient);
} else {
SnackbarModule.failure('Incorrect Username or Password');
}
splunkClient.validateCredentials().then((result) => {
if (result === true) {
localUsername.set(this.username);
localPassword.set(this.password);
localHostname.set(this.hostname);
SnackbarModule.notify('You have successfully signed in');
this.$emit('authenticated', splunkClient);
} else if (result === false) {
SnackbarModule.failure('Incorrect Username or Password');
} else {
SnackbarModule.failure(result);
this.$emit('error');
}
});
}

/** Init our fields */
Expand Down
42 changes: 29 additions & 13 deletions apps/frontend/src/components/global/upload_tabs/splunk/FileList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
:headers="headers"
item-key="guid"
:items="executions"
:search="search"
:loading="loading"
show-select
>
<template #[`item.actions`]="{item}">
Expand Down Expand Up @@ -52,12 +52,13 @@ import {
import _ from 'lodash';
import Vue from 'vue';
import Component from 'vue-class-component';
import {Prop} from 'vue-property-decorator';
import {Prop, Watch} from 'vue-property-decorator';
@Component({})
export default class FileList extends Vue {
@Prop({type: Object, required: true}) readonly splunkClient!: SplunkClient;

search = '';
search = 'index="*" "meta.subtype"=header';
loading = false;
executions: FileMetaData[] = [];
selectedExecutions: FileMetaData[] = [];

Expand All @@ -80,19 +81,34 @@ export default class FileList extends Vue {
}
];

@Watch('search')
async onUpdateSearch() {
this.mounted();
}

async mounted() {
this.executions = await getAllExecutions(this.splunkClient);
this.loading = true;
this.executions = await getAllExecutions(
this.splunkClient,
this.search
).then((executions) => {
this.loading = false;
return executions;
});
}

loadResults() {
const files = this.selectedExecutions.map((execution) => {
return getExecution(this.splunkClient, execution.guid).then((result) =>
InspecIntakeModule.loadText({
text: JSON.stringify(result),
filename: _.get(result, 'meta.filename')
}).catch((err) => {
SnackbarModule.failure(String(err));
})
async loadResults() {
this.loading = true;
const files = this.selectedExecutions.map(async (execution) => {
return getExecution(this.splunkClient, execution.guid).then(
async (result) => {
return InspecIntakeModule.loadText({
text: JSON.stringify(result),
filename: _.get(result, 'meta.filename')
}).catch((err) => {
SnackbarModule.failure(String(err));
});
}
);
});
this.$emit('got-files', files);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<AuthStep @authenticated="onAuthenticationComplete" />
<AuthStep
@authenticated="onAuthenticationComplete"
@error="errorCount += 1"
@show-help="errorCount = -1"
/>
</v-stepper-content>
<v-stepper-content step="2">
<FileList
Expand All @@ -20,6 +24,35 @@
/>
</v-stepper-content>
</v-stepper-items>
<v-overlay
:opacity="50"
absolute="absolute"
:value="errorCount >= 3 || errorCount < 0"
>
<div class="text-center">
<p>
<span v-if="errorCount > 0">
It seems you may be having trouble using the Splunk toolkit. Are you
sure that you have configured it properly?
</span>
<br />
<span>
For installation instructions and further information, check here:
</span>
<v-btn
target="_blank"
href="https://github.com/mitre/hdf-json-to-splunk/"
text
color="info"
px-0
>
<v-icon pr-2>mdi-github-circle</v-icon>
Splunk HDF Plugin
</v-btn>
</p>
<v-btn color="info" @click="errorCount = 0"> Ok </v-btn>
</div>
</v-overlay>
</v-stepper>
</template>
<script lang="ts">
Expand All @@ -37,6 +70,7 @@ import FileList from './FileList.vue';
})
export default class SplunkReader extends Vue {
step = 1;
errorCount = 0;
splunkClient: SplunkClient | null = null;

onAuthenticationComplete(splunkClient: SplunkClient) {
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/src/types/splunk-sdk/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module '@mitre/splunk-sdk-no-env';
66 changes: 47 additions & 19 deletions apps/frontend/src/utilities/splunk_util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from 'axios';
import {ExecJSON} from 'inspecjs';
import _ from 'lodash';
import {delay} from './async_util';
import {basic_auth, group_by, map_hash} from './helper_util';

export type JobID = number;
Expand Down Expand Up @@ -37,20 +38,41 @@ export class SplunkClient {

async validateCredentials(): Promise<boolean> {
return apiClient
.get(`${this.host}/services/search/jobs`, {
.get(`${this.host}/services/search/jobs?output_mode=json`, {
headers: {
Authorization: basic_auth(this.username, this.password)
},
validateStatus: () => true, // Instead of throwing a generic error we can use the response instead to create a better one
method: 'GET'
})
.then(() => {
apiClient.defaults.headers.common['Authorization'] = basic_auth(
this.username,
this.password
);
return true;
.then((response) => {
if (response.status === 200) {
apiClient.defaults.headers.common['Authorization'] = basic_auth(
this.username,
this.password
);
return true;
} else if (response.status === 401) {
return false;
} else {
// Did we get a message from Splunk?
if (_.get(response.data, 'messages')) {
return `Error Received from Splunk: ${response.data.messages
.map((message: {type: string; text: string}) => message.text)
.join(', ')}`;
}
// Something else responded, likely a corporate proxy
else {
return `Unexpected response code ${
response.status
} received, are you behind a proxy preventing you from connecting to your Splunk instance? Data received: ${_.truncate(
response.data,
{length: 256}
)}`;
}
}
})
.catch(() => false);
.catch((err) => err);
}
}

Expand All @@ -63,6 +85,7 @@ async function waitForJob(
.get(`${splunkClient.host}/services/search/jobs/${id}?output_mode=json`)
.then(({data}) => data.entry[0].content.isDone);
if (!completed) {
await delay(500);
return waitForJob(splunkClient, id);
} else {
return apiClient
Expand Down Expand Up @@ -137,10 +160,20 @@ function consolidateFilePayloads(
(ctrl) => ctrl.meta.profile_sha256
);
for (const profile of profileEvents) {
profile.controls = [];
// Get the corresponding controls, and put them into the profile
const sha = profile.meta.profile_sha256;
const corrControls = shaGroupedControls[sha] || [];
profile.controls.push(...corrControls);
profile.controls.push(
...replaceKeyValueDescriptions(
corrControls as unknown as (ExecJSON.Control &
GenericPayloadWithMetaData & {
descriptions?:
| {[key: string]: string}
| ExecJSON.ControlDescription[];
})[]
)
);
}

return exec as unknown as ExecJSON.Execution;
Expand All @@ -150,23 +183,18 @@ export async function getExecution(
splunkClient: SplunkClient,
guid: string
): Promise<ExecJSON.Execution> {
const jobId = await createSearch(
splunkClient,
`spath "meta.guid" | search "meta.guid"=${guid}`
);
const jobId = await createSearch(splunkClient, `"meta.guid"=${guid}`);
const executionPayloads = waitForJob(splunkClient, jobId);
return executionPayloads
.then((payloads) => consolidate_payloads(payloads))
.then((executions) => executions[0]);
}

export async function getAllExecutions(
splunkClient: SplunkClient
splunkClient: SplunkClient,
searchQuery: string
): Promise<FileMetaData[]> {
const jobId = await createSearch(
splunkClient,
'spath "meta.subtype" | search "meta.subtype"=header'
);
const jobId = await createSearch(splunkClient, searchQuery);
return waitForJob(splunkClient, jobId).then(
(executions: GenericPayloadWithMetaData[]) =>
executions.map((execution) => execution.meta)
Expand All @@ -179,7 +207,7 @@ export async function createSearch(
// We basically can't, and really shouldn't, do typescript here. Output is changes depending on the job called
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<JobID> {
const fullQuery = `search=search index="hdf" | ${searchString || ''}`;
const fullQuery = `search=search ${searchString || ''}`;
return apiClient({
method: 'POST',
url: `${splunkClient.host}/services/search/jobs?output_mode=json`,
Expand Down
4 changes: 3 additions & 1 deletion libs/hdf-converters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"xml2json": "ts-node data/converters/xml2json.ts"
},
"dependencies": {
"@mitre/splunk-sdk-no-env": "^1.10.0",
"@types/csv2json": "^1.4.2",
"@types/xml2js": "^0.4.9",
"aws-sdk": "^2.1046.0",
Expand All @@ -33,6 +34,7 @@
"inspecjs": "^2.6.10",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"winston": "^3.6.0",
"xml2js": "^0.4.23"
},
"devDependencies": {
Expand All @@ -48,7 +50,7 @@
},
"jest": {
"rootDir": ".",
"testTimeout": 50000,
"testTimeout": 10000000,
"transform": {
"^.+\\.ts$": "ts-jest"
}
Expand Down
Loading