Skip to content

Commit

Permalink
Merge pull request #273 from Security-Onion-Solutions/2.4/dev
Browse files Browse the repository at this point in the history
2.4.5
  • Loading branch information
jertel authored Aug 7, 2023
2 parents 5df11f1 + e0c72ae commit 564e9fd
Show file tree
Hide file tree
Showing 13 changed files with 412 additions and 57 deletions.
1 change: 1 addition & 0 deletions config/clientparameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type HuntingAction struct {
Body string `json:"body"`
Options map[string]interface{} `json:"options"`
Categories []string `json:"categories"`
JSCall string `json:"jsCall"`
}

type ToggleFilter struct {
Expand Down
21 changes: 18 additions & 3 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ <h4 v-if="loaded">{{ i18n.eventTotal }} {{ totalEvents.toLocaleString() }}</h4>
{{ i18n.quickActions }}
</v-list-item-title>
</template>
<v-list-item dense v-for="action in actions" class="custom-quick-action-button" v-if="action.enabled" :key="action.link" :href="action.background ? '' : action.linkFormatted" :title="$root.localizeMessage(action.description)" :target="$root.target(action.target)" @click="$root.performAction($event, action)">
<v-list-item dense v-for="action in actions" class="custom-quick-action-button" v-if="action.enabled" :key="action.link" :href="action.background ? '' : action.linkFormatted" :title="$root.localizeMessage(action.description)" :target="$root.target(action.target)" @click="performAction($event, action) || $root.performAction($event, action)">
<v-list-item-icon>
<v-icon :alt="action.name" color="white">{{ action.icon }}</v-icon>
</v-list-item-icon>
Expand Down Expand Up @@ -897,6 +897,21 @@ <h4 v-if="loaded">{{ i18n.eventTotal }} {{ totalEvents.toLocaleString() }}</h4>
</v-card-actions>
</v-card>
</v-dialog>

<v-dialog v-model="addToCaseDialogVisible" persistent max-width="800">
<v-card>
<v-card-title class="text-h5" v-text="i18n.actionAddToCase" />
<v-card-text>
<v-select id="case-select" v-if="addToCaseDialogVisible" v-model="selectedMruCase" :items="mruCases" :label="i18n.mruCases"></v-select>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text id="add-to-case-proceed-button" @click="addToCase(false)" v-text="i18n.add" />
<v-btn text id="add-to-case-proceed-new-tab-button" @click="addToCase(true)" v-text="i18n.addInNewTab" />
<v-btn text id="add-to-case-cancel-button" @click="cancelAddToCaseDialog()" v-text="i18n.cancel" />
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</script>

Expand Down Expand Up @@ -3054,13 +3069,13 @@ <h4 v-if="metricsEnabled">{{ i18n.gridEps }} {{ gridEps | formatCount }}</h4>
<div v-if="item.metricsEnabled" class="text-no-wrap">
<span class="filter label">{{ i18n.nodeStatusRaid }}</span>
<span :class="colorNodeStatus(item.raidStatus) + '--text'" class="filter value">
<span v-if="item.raidStatus">
<span v-if="!item.raidStatus || item.raidStatus == NodeStatusUnknown">
{{ i18n.featureRequiresAppliance }}
<a :target="$root.target('so-help')" :href="$root.parameters.docsUrl + 'grid.html'" style="text-decoration: none; cursor: 'pointer'" :title="i18n.clickForMoreInformation">
<v-icon color="info" class="mb-2">fa-circle-question</v-icon>
</a>
</span>
<span v-else v-text="$root.l$root.localizeMessage(item.raidStatus)"/>
<span v-else v-text="$root.localizeMessage(item.raidStatus)"/>
</span>
</div>
<div class="text-no-wrap">
Expand Down
4 changes: 2 additions & 2 deletions html/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,8 @@ $(document).ready(function() {
if (response) {
const redirectCookie = this.getCookie('AUTH_REDIRECT');
if ((response.headers && response.headers['content-type'] == "text/html") ||
(response.status == 401) ||
(redirectCookie != null && redirectCookie.length > 0)) {
(response.status == 401 && response.request.responseURL.indexOf('/api/') == -1) ||
(response.request.responseURL.indexOf("/login/") == -1 && redirectCookie != null && redirectCookie.length > 0)) {
this.deleteCookie('AUTH_REDIRECT');
this.showLogin();
return null
Expand Down
27 changes: 27 additions & 0 deletions html/js/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,30 @@ test('isIPv6', () => {
expect(app.isIPv6('::8')).toBe(true);
expect(app.isIPv6('::')).toBe(true);
});

function testCheckForUnauthorized(url, response, authRedirectCookie, unauthorized) {
app.showLogin = jest.fn();
app.getCookie = jest.fn(cookie => authRedirectCookie);
app.deleteCookie = jest.fn();

response.request = {responseURL: url};
var result = app.checkForUnauthorized(response);
if (unauthorized) {
expect(result).toBe(null);
expect(app.showLogin).toHaveBeenCalled();
expect(app.deleteCookie).toHaveBeenCalledWith('AUTH_REDIRECT');
} else {
expect(result).toBe(response);
}
}

test('checkForUnauthorized', () => {
testCheckForUnauthorized('/foo/', {headers: {'content-type': 'text/html'}}, null, true);
testCheckForUnauthorized('/foo/', {headers: {'content-type': 'application/json'}}, null, false);
testCheckForUnauthorized('/foo/', {status: 401}, null, true);
testCheckForUnauthorized('/foo/', {status: 200}, null, false);
testCheckForUnauthorized('/api/', {status: 401}, null, false);
testCheckForUnauthorized('/foo/', {}, '/blah', true);
testCheckForUnauthorized('/foo/', {}, null, false);
testCheckForUnauthorized('/login/', {}, '/blah', false);
});
5 changes: 5 additions & 0 deletions html/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const i18n = {
ackUndoMultipleTip: 'Reverting acknowledgment on groups of alerts may take a while and will continue in the background.',
ackUndoSingleTip: 'Reverted acknowledgement and removed from view.',
actions: 'Actions',
actionAddToCase: 'Add to Case',
actionAddToCaseHelp: 'Add to a new or existing case',
actionAlert: 'Alert',
actionAlertHelp: 'Create an alert for this event',
actionCorrelate: 'Correlate',
Expand All @@ -39,6 +41,7 @@ const i18n = {
add: 'Add',
addAttachmentHelp: 'Add a new attachment to this case',
addCommentHelp: 'Add a new comment to this case',
addInNewTab: 'Add in New Tab',
addObservableHelp: 'Add a new observable to this case',
addObservable: 'Add as new observable...',
addSuccessful: 'Added successfully!',
Expand Down Expand Up @@ -203,6 +206,7 @@ const i18n = {
copyFieldValueToClipboard: 'Copy as field:value',
copyToClipboard: 'Copy to clipboard',
create: 'Create',
createNewCase: 'Create a new case...',
custom: 'Custom',
darkMode: 'Dark Mode',
dashboards: 'Dashboards',
Expand Down Expand Up @@ -439,6 +443,7 @@ const i18n = {
model: 'Model',
module: 'Module',
months: 'months',
mruCases: 'Recently Viewed Cases',
mruQuery: 'Recently Used',
mruQueryHelp: 'This query is a user-defined query and is only available on this browser.',
na: 'N/A',
Expand Down
37 changes: 29 additions & 8 deletions html/js/routes/case.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ routes.push({ path: '/case/:id', name: 'case', component: {
},
created() {
},
async mounted() {
mounted() {
this.$root.loadParameters('case', this.initCase);
if (this.$route.params.id == 'create') {
await this.createCase();
} else {
await this.loadData();
}
this.$root.subscribe("job", this.updateJob);
this.$watch(
() => this.$route.params,
(to, prev) => {
this.loadUrlParameters();
});
},
beforeDestroy() {
this.$root.setSubtitle("");
Expand All @@ -182,7 +182,11 @@ routes.push({ path: '/case/:id', name: 'case', component: {
'$route': 'loadData',
},
methods: {
initCase(params) {
async initCase(params) {
if (this.$route.params.id === 'create') {
await this.createCase();
}

this.params = params;
this.mruCaseLimit = params["mostRecentlyUsedLimit"];
this.renderAbbreviatedCount = params["renderAbbreviatedCount"];
Expand All @@ -192,9 +196,26 @@ routes.push({ path: '/case/:id', name: 'case', component: {
}
this.analyzerNodeId = params["analyzerNodeId"];
this.loadLocalSettings();
await this.loadData();
this.resetForm('attachments');
this.resetForm('evidence');
this.resetForm('comments');

this.loadUrlParameters();
},
loadUrlParameters() {
if (this.$route.query.type) {
this.activeTab = this.$route.query.type;
}

if (this.activeTab === 'evidence' && this.$route.query.value) {
this.enableAdding('evidence');
this.$nextTick(() => {
this.associatedForms['evidence'].value = this.$route.query.value;
this.$refs['evidence'].validate();
});
window.name = encodeURIComponent(this.caseObj.id);
}
},
getAttachmentHelp() {
return this.i18n.attachmentHelp.replace("{maxUploadSizeBytes}", this.$root.formatCount(this.maxUploadSizeBytes));
Expand Down Expand Up @@ -415,7 +436,7 @@ routes.push({ path: '/case/:id', name: 'case', component: {
description: this.i18n.caseDefaultDescription,
});
if (response && response.data && response.data.id) {
this.$router.replace({ name: 'case', params: { id: response.data.id } });
this.$router.replace({ name: 'case', params: { id: response.data.id }, query: this.$route.query });
} else {
this.$root.showError(i18n.createFailed);
}
Expand Down
25 changes: 15 additions & 10 deletions html/js/routes/case.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,19 @@ beforeEach(() => {
resetPapi();
});

test('initParams', () => {
test('initParams', async () => {
comp.$route.query = {};
comp.mruCases.push({id:"123"});
comp.saveLocalSettings()
comp.mruCases = [];
comp.initCase({"foo":"bar", "mostRecentlyUsedLimit": 23});
const mock = jest.fn().mockReturnValue(Promise.resolve({ data: [] }));
comp.$root.papi['get'] = mock;

await comp.initCase({"foo":"bar", "mostRecentlyUsedLimit": 23});
expect(comp.params.foo).toBe("bar");
expect(comp.mruCaseLimit).toBe(23);
expect(comp.mruCases.length).toBe(1)
expect(comp.mruCases.length).toBe(2);
expect(mock).toHaveBeenCalledTimes(7);
});

test('addMRUCaseObj', () => {
Expand Down Expand Up @@ -125,7 +130,7 @@ test('loadAssociations', () => {
comp.caseObj = {id: 'myCaseId'};
comp.loadAssociation = jest.fn();
comp.loadAssociations();

expect(comp.loadAssociation).toHaveBeenCalledWith('comments');
expect(comp.loadAssociation).toHaveBeenCalledWith('attachments');
expect(comp.loadAssociation).toHaveBeenCalledWith('evidence');
Expand Down Expand Up @@ -171,7 +176,7 @@ expectCaseDetails = () => {
}

test('createCase', async () => {
const params = {
const params = {
"description": comp.i18n.caseDefaultDescription,
"title": comp.i18n.caseDefaultTitle,
};
Expand Down Expand Up @@ -527,7 +532,7 @@ test('startEdit', async () => {

await comp.startEdit('myFid', 'myVal', 'myRoId', 'myField', fn, ['foo'], true);

const expectedObj = {
const expectedObj = {
callback: fn,
callbackArgs: ['foo'],
field: 'myField',
Expand All @@ -536,7 +541,7 @@ test('startEdit', async () => {
orig: 'myVal',
roId: 'myRoId',
val: 'myVal',
valid: true,
valid: true,
};
expect(comp.editForm).toStrictEqual(expectedObj);
})
Expand Down Expand Up @@ -585,7 +590,7 @@ test('selectList', () => {
'customEnabled': false
},
}

const expectedList = [
'presetSeverity1',
'presetSeverity2'
Expand All @@ -604,7 +609,7 @@ test('selectList_CustomEnabledNoCustomVal', () => {
'customEnabled': true
},
}

const expectedList = [
'presetSeverity1',
'presetSeverity2',
Expand Down Expand Up @@ -854,7 +859,7 @@ test('shouldLoadAndUpdateAnalyzeJobs', async () => {

mock = mockPapi("get", { data: [job3, job1, job2]});
const showErrorMock = mockShowError();
comp.associations['evidence'] = [
comp.associations['evidence'] = [
{ id: 'artifact1' },
{ id: 'artifact2' },
];
Expand Down
Loading

0 comments on commit 564e9fd

Please sign in to comment.