Skip to content
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
24 changes: 22 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,11 @@ module.exports = {
* Security Solution overrides
*/
{
// front end typescript and javascript files only
files: ['x-pack/plugins/security_solution/public/**/*.{js,ts,tsx}'],
// front end and common typescript and javascript files only
files: [
'x-pack/plugins/security_solution/public/**/*.{js,ts,tsx}',
'x-pack/plugins/security_solution/common/**/*.{js,ts,tsx}',
],
rules: {
'import/no-nodejs-modules': 'error',
'no-restricted-imports': [
Expand Down Expand Up @@ -766,6 +769,23 @@ module.exports = {
/**
* Lists overrides
*/
{
// front end and common typescript and javascript files only
files: [
'x-pack/plugins/lists/public/**/*.{js,ts,tsx}',
'x-pack/plugins/lists/common/**/*.{js,ts,tsx}',
],
rules: {
'import/no-nodejs-modules': 'error',
'no-restricted-imports': [
'error',
{
// prevents UI code from importing server side code and then webpack including it when doing builds
patterns: ['**/server/*'],
},
],
},
},
{
// typescript and javascript for front and back end
files: ['x-pack/plugins/lists/**/*.{js,ts,tsx}'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@

/* eslint-disable @typescript-eslint/camelcase */

// TODO: You cannot import a stream from common into the front end code! CHANGE THIS
import { Readable } from 'stream';

import * as t from 'io-ts';

import { file } from '../common/schemas';
Expand All @@ -20,17 +17,3 @@ export const importListItemSchema = t.exact(
);

export type ImportListItemSchema = t.TypeOf<typeof importListItemSchema>;

// TODO: You cannot import a stream from common into the front end code! CHANGE THIS
export interface HapiReadableStream extends Readable {
hapi: {
filename: string;
};
}

/**
* Special interface since we are streaming in a file through a reader
*/
export interface ImportListItemHapiFileSchema {
file: HapiReadableStream;
}
22 changes: 16 additions & 6 deletions x-pack/plugins/lists/server/routes/import_list_item_route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Readable } from 'stream';

import { IRouter } from 'kibana/server';

import { LIST_ITEM_URL } from '../../common/constants';
Expand All @@ -13,15 +15,23 @@ import {
transformError,
validate,
} from '../siem_server_deps';
import {
ImportListItemHapiFileSchema,
importListItemQuerySchema,
importListItemSchema,
listSchema,
} from '../../common/schemas';
import { importListItemQuerySchema, importListItemSchema, listSchema } from '../../common/schemas';

import { getListClient } from '.';

export interface HapiReadableStream extends Readable {
hapi: {
filename: string;
};
}

/**
* Special interface since we are streaming in a file through a reader
*/
export interface ImportListItemHapiFileSchema {
file: HapiReadableStream;
}

export const importListItemRoute = (router: IRouter): void => {
router.post(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { TestReadable } from '../../../common/test_readable.mock';

import { BufferLines } from './buffer_lines';
import { TestReadable } from './test_readable.mock';

describe('buffer_lines', () => {
test('it can read a single line', (done) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TestReadable } from '../../../common/test_readable.mock';
import { getCallClusterMock } from '../../../common/get_call_cluster.mock';
import { ImportListItemsToStreamOptions, WriteBufferToItemsOptions } from '../items';
import { LIST_ID, LIST_ITEM_INDEX, META, TYPE, USER } from '../../../common/constants.mock';

import { TestReadable } from './test_readable.mock';

export const getImportListItemsToStreamOptionsMock = (): ImportListItemsToStreamOptions => ({
callCluster: getCallClusterMock(),
listId: LIST_ID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ export type Throttle = t.TypeOf<typeof throttle>;
export const throttleOrNull = t.union([throttle, t.null]);
export type ThrottleOrNull = t.TypeOf<typeof throttleOrNull>;

export const throttleOrNullOrUndefined = t.union([throttle, t.null, t.undefined]);
export type ThrottleOrUndefinedOrNull = t.TypeOf<typeof throttleOrNullOrUndefined>;

export const anomaly_threshold = PositiveInteger;
export type AnomalyThreshold = t.TypeOf<typeof PositiveInteger>;

Expand All @@ -156,11 +159,8 @@ export const machineLearningJobIdOrUndefined = t.union([machine_learning_job_id,
export type MachineLearningJobIdOrUndefined = t.TypeOf<typeof machineLearningJobIdOrUndefined>;

/**
* Note that this is a plain unknown object because we allow the UI
* to send us extra additional information as "meta" which can be anything.
*
* TODO: Strip away extra information and possibly even "freeze" this object
* so we have tighter control over 3rd party data structures.
* Note that this is a non-exact io-ts type as we allow extra meta information
* to be added to the meta object
*/
export const meta = t.object;
export type Meta = t.TypeOf<typeof meta>;
Expand Down Expand Up @@ -192,8 +192,10 @@ export const severityOrUndefined = t.union([severity, t.undefined]);
export type SeverityOrUndefined = t.TypeOf<typeof severityOrUndefined>;

export const status = t.keyof({ open: null, closed: null });
export type Status = t.TypeOf<typeof status>;

export const job_status = t.keyof({ succeeded: null, failed: null, 'going to run': null });
export type JobStatus = t.TypeOf<typeof job_status>;

// TODO: Create a regular expression type or custom date math part type here
export const to = t.string;
Expand Down Expand Up @@ -307,10 +309,20 @@ export const versionOrUndefined = t.union([version, t.undefined]);
export type VersionOrUndefined = t.TypeOf<typeof versionOrUndefined>;

export const last_success_at = IsoDateString;
export type LastSuccessAt = t.TypeOf<typeof IsoDateString>;

export const last_success_message = t.string;
export type LastSuccessMessage = t.TypeOf<typeof last_success_message>;

export const last_failure_at = IsoDateString;
export type LastFailureAt = t.TypeOf<typeof last_failure_at>;

export const last_failure_message = t.string;
export type LastFailureMessage = t.TypeOf<typeof last_failure_message>;

export const status_date = IsoDateString;
export type StatusDate = t.TypeOf<typeof status_date>;

export const rules_installed = PositiveInteger;
export const rules_updated = PositiveInteger;
export const status_code = PositiveInteger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,30 @@
import { CreateRulesSchema, CreateRulesSchemaDecoded } from './create_rules_schema';
import { DEFAULT_MAX_SIGNALS } from '../../../constants';

export const getCreateRulesSchemaMock = (): CreateRulesSchema => ({
description: 'some description',
export const getCreateRulesSchemaMock = (ruleId = 'rule-1'): CreateRulesSchema => ({
description: 'Detecting root and admin users',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'query',
risk_score: 55,
language: 'kuery',
rule_id: 'rule-1',
rule_id: ruleId,
});

export const getCreateMlRulesSchemaMock = (ruleId = 'rule-1') => {
const { query, language, index, ...mlParams } = getCreateRulesSchemaMock(ruleId);

return {
...mlParams,
type: 'machine_learning',
anomaly_threshold: 58,
machine_learning_job_id: 'typical-ml-job-id',
};
};

export const getCreateRulesSchemaDecodedMock = (): CreateRulesSchemaDecoded => ({
description: 'some description',
description: 'Detecting root and admin users',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,27 @@
import { ImportRulesSchema, ImportRulesSchemaDecoded } from './import_rules_schema';
import { DEFAULT_MAX_SIGNALS } from '../../../constants';

export const getImportRulesSchemaMock = (): ImportRulesSchema => ({
export const getImportRulesSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema => ({
description: 'some description',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'query',
risk_score: 55,
language: 'kuery',
rule_id: 'rule-1',
rule_id: ruleId,
});

export const getImportRulesWithIdSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema => ({
id: '6afb8ce1-ea94-4790-8653-fd0b021d2113',
description: 'some description',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'query',
risk_score: 55,
language: 'kuery',
rule_id: ruleId,
});

export const getImportRulesSchemaDecodedMock = (): ImportRulesSchemaDecoded => ({
Expand All @@ -42,3 +54,22 @@ export const getImportRulesSchemaDecodedMock = (): ImportRulesSchemaDecoded => (
rule_id: 'rule-1',
immutable: false,
});

/**
* Given an array of rules, builds an NDJSON string of rules
* as we might import/export
* @param rules Array of rule objects with which to generate rule JSON
*/
export const rulesToNdJsonString = (rules: ImportRulesSchema[]) => {
return rules.map((rule) => JSON.stringify(rule)).join('\r\n');
};

/**
* Given an array of rule IDs, builds an NDJSON string of rules
* as we might import
* @param ruleIds Array of ruleIds with which to generate rule JSON
*/
export const ruleIdsToNdJsonString = (ruleIds: string[]) => {
const rules = ruleIds.map((ruleId) => getImportRulesSchemaMock(ruleId));
return rulesToNdJsonString(rules);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { ErrorSchema } from './error_schema';

export const getErrorSchemaMock = (
id: string = '819eded6-e9c8-445b-a647-519aea39e063'
): ErrorSchema => ({
id,
error: {
status_code: 404,
message: 'id: "819eded6-e9c8-445b-a647-519aea39e063" not found',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
import { left } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';

import { getErrorPayload } from './__mocks__/utils';
import { errorSchema, ErrorSchema } from './error_schema';
import { exactCheck } from '../../../exact_check';
import { foldLeftRight, getPaths } from '../../../test_utils';
import { getErrorSchemaMock } from './error_schema.mocks';

describe('error_schema', () => {
test('it should validate an error with a UUID given for id', () => {
const error = getErrorPayload();
const decoded = errorSchema.decode(getErrorPayload());
const error = getErrorSchemaMock();
const decoded = errorSchema.decode(getErrorSchemaMock());
const checked = exactCheck(error, decoded);
const message = pipe(checked, foldLeftRight);

Expand All @@ -24,7 +24,7 @@ describe('error_schema', () => {
});

test('it should validate an error with a plain string given for id since sometimes we echo the user id which might not be a UUID back out to them', () => {
const error = getErrorPayload('fake id');
const error = getErrorSchemaMock('fake id');
const decoded = errorSchema.decode(error);
const checked = exactCheck(error, decoded);
const message = pipe(checked, foldLeftRight);
Expand All @@ -35,7 +35,7 @@ describe('error_schema', () => {

test('it should NOT validate an error when it has extra data next to a valid payload element', () => {
type InvalidError = ErrorSchema & { invalid_extra_data?: string };
const error: InvalidError = getErrorPayload();
const error: InvalidError = getErrorSchemaMock();
error.invalid_extra_data = 'invalid_extra_data';
const decoded = errorSchema.decode(error);
const checked = exactCheck(error, decoded);
Expand All @@ -46,7 +46,7 @@ describe('error_schema', () => {
});

test('it should NOT validate an error when it has required elements deleted from it', () => {
const error = getErrorPayload();
const error = getErrorSchemaMock();
delete error.error;
const decoded = errorSchema.decode(error);
const checked = exactCheck(error, decoded);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { FindRulesSchema } from './find_rules_schema';
import { getRulesSchemaMock } from './rules_schema.mocks';

export const getFindRulesSchemaMock = (): FindRulesSchema => ({
page: 1,
perPage: 1,
total: 1,
data: [getRulesSchemaMock()],
});
Loading