[Asset Management] Osquery autocomplete (#94255)
* added osquery mode to autocomplete * clean up and formatting * arm wrestling with the compiler * more fighting with ace types * Delete v4.5.0.json removed unused schema file * playing the hokey pokey with import statements * lazy load the schema file * remove include rule now that we are lazy loading schema json * update out of date comment * reduce schema file to what is currently being used, add script for formatting generated api files * added a readme, and points the compiler at the scripts directory * swip-swapped the argument order, fixed linting complaints Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e1363855bf
commit
ce7a0bb8fc
19
x-pack/plugins/osquery/public/editor/ace_types.ts
Normal file
19
x-pack/plugins/osquery/public/editor/ace_types.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ace#define is not defined in the published types, so we define our own
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
export interface AceInterface {
|
||||||
|
define: (
|
||||||
|
name: string,
|
||||||
|
deps: string[],
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
cb: (acequire: (name: string) => any, exports: any) => void
|
||||||
|
) => void;
|
||||||
|
}
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { EuiCodeEditor } from '@elastic/eui';
|
import { EuiCodeEditor } from '@elastic/eui';
|
||||||
import 'brace/mode/sql';
|
|
||||||
import 'brace/theme/tomorrow';
|
import 'brace/theme/tomorrow';
|
||||||
import 'brace/ext/language_tools';
|
|
||||||
|
import './osquery_mode.ts';
|
||||||
|
|
||||||
const EDITOR_SET_OPTIONS = {
|
const EDITOR_SET_OPTIONS = {
|
||||||
enableBasicAutocompletion: true,
|
enableBasicAutocompletion: true,
|
||||||
|
@ -36,7 +36,7 @@ const OsqueryEditorComponent: React.FC<OsqueryEditorProps> = ({ defaultValue, on
|
||||||
return (
|
return (
|
||||||
<EuiCodeEditor
|
<EuiCodeEditor
|
||||||
value={defaultValue}
|
value={defaultValue}
|
||||||
mode="sql"
|
mode="osquery"
|
||||||
theme="tomorrow"
|
theme="tomorrow"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
name="osquery_editor"
|
name="osquery_editor"
|
||||||
|
|
184
x-pack/plugins/osquery/public/editor/osquery_highlight_rules.ts
Normal file
184
x-pack/plugins/osquery/public/editor/osquery_highlight_rules.ts
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ace from 'brace';
|
||||||
|
import 'brace/ext/language_tools';
|
||||||
|
import { AceInterface } from './ace_types';
|
||||||
|
import { getOsqueryTableNames } from './osquery_tables';
|
||||||
|
|
||||||
|
const osqueryTables = getOsqueryTableNames().join('|');
|
||||||
|
|
||||||
|
const keywords = [
|
||||||
|
'select',
|
||||||
|
'insert',
|
||||||
|
'update',
|
||||||
|
'delete',
|
||||||
|
'from',
|
||||||
|
'where',
|
||||||
|
'and',
|
||||||
|
'or',
|
||||||
|
'group',
|
||||||
|
'by',
|
||||||
|
'order',
|
||||||
|
'limit',
|
||||||
|
'offset',
|
||||||
|
'having',
|
||||||
|
'as',
|
||||||
|
'case',
|
||||||
|
'when',
|
||||||
|
'else',
|
||||||
|
'end',
|
||||||
|
'type',
|
||||||
|
'left',
|
||||||
|
'right',
|
||||||
|
'join',
|
||||||
|
'on',
|
||||||
|
'outer',
|
||||||
|
'desc',
|
||||||
|
'asc',
|
||||||
|
'union',
|
||||||
|
'create',
|
||||||
|
'table',
|
||||||
|
'primary',
|
||||||
|
'key',
|
||||||
|
'if',
|
||||||
|
'foreign',
|
||||||
|
'not',
|
||||||
|
'references',
|
||||||
|
'default',
|
||||||
|
'null',
|
||||||
|
'inner',
|
||||||
|
'cross',
|
||||||
|
'natural',
|
||||||
|
'database',
|
||||||
|
'drop',
|
||||||
|
'grant',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
const builtinConstants = ['true', 'false'].join('|');
|
||||||
|
|
||||||
|
const builtinFunctions = [
|
||||||
|
'avg',
|
||||||
|
'count',
|
||||||
|
'first',
|
||||||
|
'last',
|
||||||
|
'max',
|
||||||
|
'min',
|
||||||
|
'sum',
|
||||||
|
'ucase',
|
||||||
|
'lcase',
|
||||||
|
'mid',
|
||||||
|
'len',
|
||||||
|
'round',
|
||||||
|
'rank',
|
||||||
|
'now',
|
||||||
|
'format',
|
||||||
|
'coalesce',
|
||||||
|
'ifnull',
|
||||||
|
'isnull',
|
||||||
|
'nvl',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
const dataTypes = [
|
||||||
|
'int',
|
||||||
|
'numeric',
|
||||||
|
'decimal',
|
||||||
|
'date',
|
||||||
|
'varchar',
|
||||||
|
'char',
|
||||||
|
'bigint',
|
||||||
|
'float',
|
||||||
|
'double',
|
||||||
|
'bit',
|
||||||
|
'binary',
|
||||||
|
'text',
|
||||||
|
'set',
|
||||||
|
'timestamp',
|
||||||
|
'money',
|
||||||
|
'real',
|
||||||
|
'number',
|
||||||
|
'integer',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
// This is gross, but the types exported by brace are lagging and incorrect: https://github.com/thlorenz/brace/issues/182
|
||||||
|
((ace as unknown) as AceInterface).define(
|
||||||
|
'ace/mode/osquery_highlight_rules',
|
||||||
|
['require', 'exports', 'ace/mode/sql_highlight_rules'],
|
||||||
|
function (acequire, exports) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const SqlHighlightRules = acequire('./sql_highlight_rules').SqlHighlightRules;
|
||||||
|
|
||||||
|
class OsqueryHighlightRules extends SqlHighlightRules {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
constructor(...args: any) {
|
||||||
|
super(...args);
|
||||||
|
const keywordMapper = this.createKeywordMapper(
|
||||||
|
{
|
||||||
|
'osquery-token': osqueryTables,
|
||||||
|
'support.function': builtinFunctions,
|
||||||
|
keyword: keywords,
|
||||||
|
'constant.language': builtinConstants,
|
||||||
|
'storage.type': dataTypes,
|
||||||
|
},
|
||||||
|
'identifier',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$rules = {
|
||||||
|
start: [
|
||||||
|
{
|
||||||
|
token: 'comment',
|
||||||
|
regex: '--.*$',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'comment',
|
||||||
|
start: '/\\*',
|
||||||
|
end: '\\*/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'string', // " string
|
||||||
|
regex: '".*?"',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'string', // ' string
|
||||||
|
regex: "'.*?'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'constant.numeric', // float
|
||||||
|
regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: keywordMapper,
|
||||||
|
regex: '[a-zA-Z_$][a-zA-Z0-9_$]*\\b',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'keyword.operator',
|
||||||
|
regex: '\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'paren.lparen',
|
||||||
|
regex: '[\\(]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'paren.rparen',
|
||||||
|
regex: '[\\)]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: 'text',
|
||||||
|
regex: '\\s+',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.normalizeRules();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.OsqueryHighlightRules = OsqueryHighlightRules;
|
||||||
|
}
|
||||||
|
);
|
34
x-pack/plugins/osquery/public/editor/osquery_mode.ts
Normal file
34
x-pack/plugins/osquery/public/editor/osquery_mode.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ace from 'brace';
|
||||||
|
import 'brace/mode/sql';
|
||||||
|
import 'brace/ext/language_tools';
|
||||||
|
import { AceInterface } from './ace_types';
|
||||||
|
import './osquery_highlight_rules';
|
||||||
|
|
||||||
|
((ace as unknown) as AceInterface).define(
|
||||||
|
'ace/mode/osquery',
|
||||||
|
['require', 'exports', 'ace/mode/sql', 'ace/mode/osquery_highlight_rules'],
|
||||||
|
function (acequire, exports) {
|
||||||
|
const TextMode = acequire('./sql').Mode;
|
||||||
|
const OsqueryHighlightRules = acequire('./osquery_highlight_rules').OsqueryHighlightRules;
|
||||||
|
|
||||||
|
class Mode extends TextMode {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args);
|
||||||
|
this.HighlightRules = OsqueryHighlightRules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mode.prototype.lineCommentStart = '--';
|
||||||
|
Mode.prototype.$id = 'ace/mode/osquery';
|
||||||
|
|
||||||
|
exports.Mode = Mode;
|
||||||
|
}
|
||||||
|
);
|
File diff suppressed because one or more lines are too long
30
x-pack/plugins/osquery/public/editor/osquery_tables.ts
Normal file
30
x-pack/plugins/osquery/public/editor/osquery_tables.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { flatMap, sortBy } from 'lodash';
|
||||||
|
|
||||||
|
type TablesJSON = Array<{
|
||||||
|
name: string;
|
||||||
|
}>;
|
||||||
|
export const normalizeTables = (tablesJSON: TablesJSON) => {
|
||||||
|
return sortBy(tablesJSON, (table) => {
|
||||||
|
return table.name;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let osqueryTables: TablesJSON | null = null;
|
||||||
|
export const getOsqueryTables = () => {
|
||||||
|
if (!osqueryTables) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
osqueryTables = normalizeTables(require('./osquery_schema/v4.6.0.json'));
|
||||||
|
}
|
||||||
|
return osqueryTables;
|
||||||
|
};
|
||||||
|
export const getOsqueryTableNames = () =>
|
||||||
|
flatMap(getOsqueryTables(), (table) => {
|
||||||
|
return table.name;
|
||||||
|
});
|
10
x-pack/plugins/osquery/scripts/readme.md
Normal file
10
x-pack/plugins/osquery/scripts/readme.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
### Schema formatter
|
||||||
|
|
||||||
|
In order to manage the size of the osquery schema files, there is a script
|
||||||
|
available to extract only the currently used fields (this selection is
|
||||||
|
currently manually curated). This assumes the targeted schema files will be in
|
||||||
|
`public/editor/osquery_schema`.
|
||||||
|
|
||||||
|
```
|
||||||
|
node scripts/schema_formatter --schema_version=v4.6.0
|
||||||
|
```
|
9
x-pack/plugins/osquery/scripts/schema_formatter/index.js
Normal file
9
x-pack/plugins/osquery/scripts/schema_formatter/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('../../../../../src/setup_node_env');
|
||||||
|
require('./script');
|
55
x-pack/plugins/osquery/scripts/schema_formatter/script.ts
Normal file
55
x-pack/plugins/osquery/scripts/schema_formatter/script.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { run } from '@kbn/dev-utils';
|
||||||
|
interface DestField {
|
||||||
|
[key: string]: boolean | DestField;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(
|
||||||
|
async ({ flags }) => {
|
||||||
|
const schemaPath = path.resolve('../../public/editor/osquery_schema/');
|
||||||
|
const schemaFile = path.join(schemaPath, flags.schema_version as string);
|
||||||
|
const schemaData = await require(schemaFile);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function pullFields(destSchema: DestField, source: { [key: string]: any }) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const dest: { [key: string]: any } = {};
|
||||||
|
Object.keys(destSchema).forEach((key) => {
|
||||||
|
switch (typeof source[key]) {
|
||||||
|
case 'object':
|
||||||
|
dest[key] = pullFields(destSchema[key] as DestField, source[key]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dest[key] = source[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapFunc = pullFields.bind(null, { name: true });
|
||||||
|
const formattedSchema = schemaData.map(mapFunc);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(schemaPath, `${flags.schema_version}-formatted`),
|
||||||
|
JSON.stringify(formattedSchema)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: `
|
||||||
|
Script for formatting generated osquery API schema JSON file.
|
||||||
|
`,
|
||||||
|
flags: {
|
||||||
|
string: ['schema_version'],
|
||||||
|
help: `
|
||||||
|
--schema_version The semver string for the schema file located in public/editor/osquery_schema
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
|
@ -11,6 +11,7 @@
|
||||||
// add all the folders contains files to be compiled
|
// add all the folders contains files to be compiled
|
||||||
"common/**/*",
|
"common/**/*",
|
||||||
"public/**/*",
|
"public/**/*",
|
||||||
|
"scripts/**/*",
|
||||||
"server/**/*"
|
"server/**/*"
|
||||||
],
|
],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
Loading…
Reference in a new issue