Skip to content

Commit

Permalink
Merge branch 'main' into issue-123550-fix-broken-weak-links-on-import
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Mar 17, 2022
2 parents 7644734 + f4c4fd6 commit 8d0a571
Show file tree
Hide file tree
Showing 224 changed files with 3,973 additions and 2,079 deletions.
12 changes: 6 additions & 6 deletions src/dev/precommit_hook/casing_check_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ export const TEMPORARILY_IGNORED_PATHS = [
'x-pack/plugins/monitoring/public/icons/health-green.svg',
'x-pack/plugins/monitoring/public/icons/health-red.svg',
'x-pack/plugins/monitoring/public/icons/health-yellow.svg',
'x-pack/plugins/reporting/server/export_types/common/assets/fonts/noto/NotoSansCJKtc-Medium.ttf',
'x-pack/plugins/reporting/server/export_types/common/assets/fonts/noto/NotoSansCJKtc-Regular.ttf',
'x-pack/plugins/reporting/server/export_types/common/assets/fonts/roboto/Roboto-Italic.ttf',
'x-pack/plugins/reporting/server/export_types/common/assets/fonts/roboto/Roboto-Medium.ttf',
'x-pack/plugins/reporting/server/export_types/common/assets/fonts/roboto/Roboto-Regular.ttf',
'x-pack/plugins/reporting/server/export_types/common/assets/img/logo-grey.png',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Medium.ttf',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Regular.ttf',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Italic.ttf',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Medium.ttf',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Regular.ttf',
'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/img/logo-grey.png',
];
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ import { RouteDependencies } from '../../../';
import { Body, Query } from './validation_config';

function toURL(base: string, path: string) {
const [p, query = ''] = path.split('?');

// if there is a '+' sign in query e.g. ?q=create_date:[2020-05-10T08:00:00.000+08:00 TO *]
// node url encodes it as a whitespace which results in a faulty request
// we need to replace '+' with '%2b' to encode it correctly
if (/\+/g.test(query)) {
path = `${p}?${query.replace(/\+/g, '%2b')}`;
}
const urlResult = new url.URL(`${trimEnd(base, '/')}/${trimStart(path, '/')}`);
// Appending pretty here to have Elasticsearch do the JSON formatting, as doing
// in JS can lead to data loss (7.0 will get munged into 7, thus losing indication of
Expand Down Expand Up @@ -116,7 +124,7 @@ export const createHandler =
}: RouteDependencies): RequestHandler<unknown, Query, Body> =>
async (ctx, request, response) => {
const { body, query } = request;
const { path, method, withProductOrigin } = query;
const { method, path, withProductOrigin } = query;

if (kibanaVersion.major < 8) {
// The "console.proxyFilter" setting in kibana.yaml has been deprecated in 8.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { createHandler } from './create_handler';

describe('Console Proxy Route', () => {
let request: (method: string, path: string) => Promise<IKibanaResponse> | IKibanaResponse;
const proxyRequestMock = requestModule.proxyRequest as jest.Mock;

beforeEach(() => {
(requestModule.proxyRequest as jest.Mock).mockResolvedValue(createResponseStub('foo'));

Expand All @@ -39,27 +41,40 @@ describe('Console Proxy Route', () => {
describe('contains full url', () => {
it('treats the url as a path', async () => {
await request('GET', 'http://evil.com/test');
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
expect(proxyRequestMock.mock.calls.length).toBe(1);
const [[args]] = (requestModule.proxyRequest as jest.Mock).mock.calls;
expect(args.uri.href).toBe('http://localhost:9200/http://evil.com/test?pretty=true');
});
});
describe('starts with a slash', () => {
it('combines well with the base url', async () => {
await request('GET', '/index/id');
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
expect(proxyRequestMock.mock.calls.length).toBe(1);
const [[args]] = (requestModule.proxyRequest as jest.Mock).mock.calls;
expect(args.uri.href).toBe('http://localhost:9200/index/id?pretty=true');
});
});
describe(`doesn't start with a slash`, () => {
it('combines well with the base url', async () => {
await request('GET', 'index/id');
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
expect(proxyRequestMock.mock.calls.length).toBe(1);
const [[args]] = (requestModule.proxyRequest as jest.Mock).mock.calls;
expect(args.uri.href).toBe('http://localhost:9200/index/id?pretty=true');
});
});
describe('contains special characters', () => {
it('correctly encodes plus sign', async () => {
const path = '/_search?q=create_date:[2022-03-10T08:00:00.000+08:00 TO *]';

const { status } = await request('GET', path);
expect(status).toBe(200);
expect(proxyRequestMock.mock.calls.length).toBe(1);
const [[args]] = proxyRequestMock.mock.calls;
expect(args.uri.search).toEqual(
'?q=create_date%3A%5B2022-03-10T08%3A00%3A00.000%2B08%3A00+TO+*%5D&pretty=true'
);
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '../../../../common/tutorial/instructions/apm_agent_instructions';
import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
// TODO: Uncomment once https://github.com/elastic/beats/issues/29631 has been closed
// import { JavaRuntimeAttachment } from './runtime_attachment/supported_agents/java_runtime_attachment';
import { JavaRuntimeAttachment } from './runtime_attachment/supported_agents/java_runtime_attachment';
import {
NewPackagePolicy,
PackagePolicy,
Expand Down Expand Up @@ -56,8 +56,7 @@ export const ApmAgentInstructionsMappings: Array<{
title: 'Java',
variantId: 'java',
createAgentInstructions: createJavaAgentInstructions,
// TODO: Uncomment once https://github.com/elastic/beats/issues/29631 has been closed
// AgentRuntimeAttachment: JavaRuntimeAttachment,
AgentRuntimeAttachment: JavaRuntimeAttachment,
},
{
agentName: 'rum-js',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const getFakeFindings = (): CspFinding & { id: string } => ({
uid: chance.string(),
mode: chance.string(),
},
run_id: chance.string(),
cycle_id: chance.string(),
host: {} as any,
ecs: {} as any,
'@timestamp': new Date().toISOString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// TODO: this needs to be defined in a versioned schema
export interface CspFinding {
'@timestamp': string;
run_id: string;
cycle_id: string;
result: CspFindingResult;
resource: CspFindingResource;
rule: CspRule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,21 +155,21 @@ describe('findings API', () => {

mockEsClient.search.mockResolvedValueOnce(
// @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values
elasticsearchClientMock.createSuccessTransportRequestPromise({
{
aggregations: {
group: {
buckets: [
{
group_docs: {
hits: {
hits: [{ fields: { 'run_id.keyword': ['randomId1'] } }],
hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }],
},
},
},
],
},
},
})
}
);

const [context, req, res] = [mockContext, mockRequest, mockResponse];
Expand All @@ -183,7 +183,7 @@ describe('findings API', () => {
expect(handlerArgs).toMatchObject({
query: {
bool: {
filter: [{ term: { 'run_id.keyword': 'randomId1' } }],
filter: [{ terms: { 'cycle_id.keyword': ['randomId1'] } }],
},
},
});
Expand Down Expand Up @@ -348,5 +348,109 @@ describe('findings API', () => {
_source: ['field1', 'field2', 'field3'],
});
});

it('takes dslQuery and validate the conversion to esQuery filter', async () => {
const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser;
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();
const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};

defineFindingsIndexRoute(router, cspContext);

const [_, handler] = router.get.mock.calls[0];
const mockContext = getMockCspContext(mockEsClient);
const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest({
query: {
kquery: 'result.evaluation.keyword:failed',
},
});

const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);
const handlerArgs = mockEsClient.search.mock.calls[0][0];

expect(handlerArgs).toMatchObject({
query: {
bool: {
filter: [
{
bool: {
minimum_should_match: 1,
should: [{ match: { 'result.evaluation.keyword': 'failed' } }],
},
},
],
},
},
});
});

it('takes dslQuery and latest_cycle filter validate the conversion to esQuery filter', async () => {
const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser;
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();
const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};

defineFindingsIndexRoute(router, cspContext);
const [_, handler] = router.get.mock.calls[0];

const mockContext = getMockCspContext(mockEsClient);
const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest({
query: {
kquery: 'result.evaluation.keyword:failed',
latest_cycle: true,
},
});

mockEsClient.search.mockResolvedValueOnce(
// @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values
{
aggregations: {
group: {
buckets: [
{
group_docs: {
hits: {
hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }],
},
},
},
],
},
},
}
);

const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

const handlerArgs = mockEsClient.search.mock.calls[1][0];
// console.log(handlerArgs.query.bool);
expect(handlerArgs).toMatchObject({
query: {
bool: {
filter: [
{
bool: {
should: [{ match: { 'result.evaluation.keyword': 'failed' } }],
minimum_should_match: 1,
},
},
{ terms: { 'cycle_id.keyword': ['randomId1'] } },
],
},
},
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
* 2.0.
*/

import type { IRouter } from 'src/core/server';
import type { IRouter, Logger } from 'src/core/server';
import { SearchRequest, QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
import { QueryDslBoolQuery } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { schema as rt, TypeOf } from '@kbn/config-schema';
import type { SortOrder } from '@elastic/elasticsearch/lib/api/types';
import { transformError } from '@kbn/securitysolution-es-utils';
Expand Down Expand Up @@ -50,18 +52,48 @@ const getFindingsEsQuery = (
};
};

const buildQueryRequest = (latestCycleIds?: string[]): QueryDslQueryContainer => {
let filterPart: QueryDslQueryContainer = { match_all: {} };
const buildLatestCycleFilter = (
filter: QueryDslQueryContainer[],
latestCycleIds?: string[]
): QueryDslQueryContainer[] => {
if (!!latestCycleIds) {
const filter = latestCycleIds.map((latestCycleId) => ({
term: { 'run_id.keyword': latestCycleId },
}));
filterPart = { bool: { filter } };
filter.push({
terms: { 'cycle_id.keyword': latestCycleIds },
});
}
return filter;
};

return {
...filterPart,
const convertKqueryToElasticsearchQuery = (
kquery: string | undefined,
logger: Logger
): QueryDslQueryContainer[] => {
let dslFilterQuery: QueryDslBoolQuery['filter'];
try {
dslFilterQuery = kquery ? toElasticsearchQuery(fromKueryExpression(kquery)) : [];
if (!Array.isArray(dslFilterQuery)) {
dslFilterQuery = [dslFilterQuery];
}
} catch (err) {
logger.warn(`Invalid kuery syntax for the filter (${kquery}) error: ${err.message}`);
throw err;
}
return dslFilterQuery;
};

const buildQueryRequest = (
kquery: string | undefined,
latestCycleIds: string[] | undefined,
logger: Logger
): QueryDslQueryContainer => {
const kqueryFilter = convertKqueryToElasticsearchQuery(kquery, logger);
const filter = buildLatestCycleFilter(kqueryFilter, latestCycleIds);
const query = {
bool: {
filter,
},
};
return query;
};

const buildOptionsRequest = (queryParams: FindingsQuerySchema): FindingsOptions => ({
Expand All @@ -87,15 +119,20 @@ export const defineFindingsIndexRoute = (router: IRouter, cspContext: CspAppCont
? await getLatestCycleIds(esClient, cspContext.logger)
: undefined;

const query = buildQueryRequest(latestCycleIds);
if (request.query.latest_cycle === true && latestCycleIds === undefined) {
return response.ok({ body: [] });
}

const query = buildQueryRequest(request.query.kquery, latestCycleIds, cspContext.logger);
const esQuery = getFindingsEsQuery(query, options);

const findings = await esClient.search(esQuery, { meta: true });
const hits = findings.body.hits.hits;
const findings = await esClient.search(esQuery);
const hits = findings.hits.hits;

return response.ok({ body: hits });
} catch (err) {
const error = transformError(err);
cspContext.logger.error(`Failed to fetch Findings ${error.message}`);
return response.customError({
body: { message: error.message },
statusCode: error.statusCode,
Expand Down Expand Up @@ -129,4 +166,8 @@ export const findingsInputSchema = rt.object({
* The fields in the entity to return in the response
*/
fields: rt.maybe(rt.string()),
/**
* kql query
*/
kquery: rt.maybe(rt.string()),
});
Loading

0 comments on commit 8d0a571

Please sign in to comment.