Skip to content

Commit

Permalink
add helper
Browse files Browse the repository at this point in the history
Signed-off-by: Anan Zhuang <[email protected]>
  • Loading branch information
ananzh committed Jun 3, 2021
1 parent 68b0db0 commit ec28338
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { i18n } from '@osd/i18n';
// Applies to unresolved arguments in the AST
export default function repositionArguments(functionDef, unorderedArgs) {
const args = [];
console.log('functionDef', functionDef);

_.each(unorderedArgs, function (unorderedArg, i) {
let argDef;
Expand Down Expand Up @@ -65,7 +66,9 @@ export default function repositionArguments(functionDef, unorderedArgs) {
}
value = unorderedArg.value;
} else {
console.log('functionDef', functionDef);
argDef = functionDef.args[i];
console.log('functionDef', functionDef);
storeAsArray = argDef.multi;
targetIndex = i;
value = unorderedArg;
Expand Down
75 changes: 2 additions & 73 deletions src/plugins/vis_type_timeline/server/series_functions/graphite.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,85 +35,14 @@ import _ from 'lodash';
import fetch from 'node-fetch';
import moment from 'moment';
import Datasource from '../lib/classes/datasource';
import dns from 'dns-sync';
import IPCIDR from 'ip-cidr';
import { isValidConfig } from './graphite_helper';

const MISS_CHECKLIST_MESSAGE = `Please configure on the opensearch_dashbpards.yml file.
You can always enable the default allowlist configuration.`;

const INVALID_URL_MESSAGE = `The Graphite URL/IP provided by you is invalid.
const INVALID_URL_MESSAGE = `The Graphite URL provided by you is invalid.
Please update your config from OpenSearch Dashboards's Advanced Setting.`;

/**
* Resolve hostname to IP address
* @param {object} urlObject
* @returns {string} configuredIP
* or null if it cannot be resolve
* According to RFC, all IPv6 IP address needs to be in []
* such as [::1]
* So if we detect a IPv6 address, we remove brackets
*/
function getIpAddress(urlObject) {
const hostname = urlObject.hostname;
const configuredIP = dns.resolve(hostname);
if (configuredIP) {
return configuredIP;
}
if (hostname.startsWith('[') && hostname.endsWith(']')) {
return hostname.substr(1).slice(0, -1);
}
return null;
}

/**
* Check whether customer input URL is blocked
* This function first check the format of URL, URL has be in the format as
* scheme://server/path/resource otherwise an TypeError would be thrown
* Then IPCIDR check if a specific IP address fall in the
* range of an IP address block
* @param {string} configuredUrls
* @param {Array|string} blockedIPs
* @returns {boolean} true if the configuredUrl is blocked
*/
function isBlockedURL(configuredUrl, blockedIPs) {
let configuredUrlObject;
try {
configuredUrlObject = new URL(configuredUrl);
} catch (err) {
return false;
}

const ip = getIpAddress(configuredUrlObject);
if (!ip) {
return false;
}

const isBlocked = blockedIPs.some((blockedIP) => new IPCIDR(blockedIP).contains(ip));
return isBlocked;
}

/**
* Check configured url using blocklist and allowlist
* If allowlist is used, return false if allowlist does not contain configured url
* If blocklist is used, return false if blocklist contains configured url
* If both allowlist and blocklist are used, check blocklist first then allowlist
* @param {Array|string} blockedIPs
* @param {Array|string} allowedUrls
* @param {string} configuredUrls
* @returns {boolean} true if the configuredUrl is valid
*/
function isValidConfig(blockedIPs, allowedUrls, configuredUrl) {
if (blockedIPs.length === 0) {
if (!allowedUrls.includes(configuredUrl)) return false;
} else if (allowedUrls.length === 0) {
if (isBlockedURL(configuredUrl, blockedIPs)) return false;
} else {
if (isBlockedURL(configuredUrl, blockedIPs) || !allowedUrls.includes(configuredUrl))
return false;
}
return true;
}

export default new Datasource('graphite', {
args: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import fn from './graphite';
const MISS_CHECKLIST_MESSAGE = `Please configure on the opensearch_dashbpards.yml file.
You can always enable the default allowlist configuration.`;

const INVALID_URL_MESSAGE = `The Graphite URL/IP provided by you is invalid.
const INVALID_URL_MESSAGE = `The Graphite URL provided by you is invalid.
Please update your config from OpenSearch Dashboards's Advanced Setting.`;

jest.mock('node-fetch', () => (url) => {
Expand Down Expand Up @@ -102,7 +102,7 @@ describe('graphite', function () {
});
});

it('setting with matched allowlist url should return result ', function () {
it('setting with matched allowlist url should return result', function () {
return invoke(fn, [], {
settings: {
'timeline:graphite.url': 'https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite',
Expand All @@ -114,7 +114,7 @@ describe('graphite', function () {
});
});

it('setting with unmatched allowlist url should return error message ', function () {
it('setting with unmatched allowlist url should return error message', function () {
return invoke(fn, [], {
settings: { 'timeline:graphite.url': 'http://127.0.0.1' },
allowedGraphiteUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
Expand Down Expand Up @@ -183,4 +183,28 @@ describe('graphite', function () {
expect(e.message).to.includes('maximum redirect reached');
});
});

it('with both allowlist and blocklist, setting not in blocklist but in allowlist should return result', function () {
return invoke(fn, [], {
settings: {
'timeline:graphite.url': 'https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite',
},
allowedGraphiteUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
blockedGraphiteIPs: ['127.0.0.0/8'],
}).then((result) => {
expect(result.output.list.length).to.eql(1);
});
});

it('with conflict allowlist and blocklist, setting in blocklist and in allowlist should return error message', function () {
return invoke(fn, [], {
settings: {
'timeline:graphite.url': 'http://127.0.0.1',
},
allowedGraphiteUrls: ['http://127.0.0.1'],
blockedGraphiteIPs: ['127.0.0.0/8'],
}).catch((e) => {
expect(e.message).to.eql(INVALID_URL_MESSAGE);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
import dns from 'dns-sync';
import IPCIDR from 'ip-cidr';
/**
* Resolve hostname to IP address
* @param {object} urlObject
* @returns {string} configuredIP
* or null if it cannot be resolve
* According to RFC, all IPv6 IP address needs to be in []
* such as [::1]
* So if we detect a IPv6 address, we remove brackets
*/
function getIpAddress(urlObject) {
const hostname = urlObject.hostname;
const configuredIP = dns.resolve(hostname);
if (configuredIP) {
return configuredIP;
}
if (hostname.startsWith('[') && hostname.endsWith(']')) {
return hostname.substr(1).slice(0, -1);
}
return null;
}
/**
* Check whether customer input URL is blocked
* This function first check the format of URL, URL has be in the format as
* scheme://server/path/resource otherwise an TypeError would be thrown
* Then IPCIDR check if a specific IP address fall in the
* range of an IP address block
* @param {string} configuredUrls
* @param {Array|string} blockedIPs
* @returns {boolean} true if the configuredUrl is blocked
*/
function isBlockedURL(configuredUrl, blockedIPs) {
let configuredUrlObject;
try {
configuredUrlObject = new URL(configuredUrl);
} catch (err) {
return true;
}
const ip = exports.getIpAddress(configuredUrlObject);
if (!ip) {
return true;
}
const isBlocked = blockedIPs.some((blockedIP) => new IPCIDR(blockedIP).contains(ip));
return isBlocked;
}
/**
* Check configured url using blocklist and allowlist
* If allowlist is used, return false if allowlist does not contain configured url
* If blocklist is used, return false if blocklist contains configured url
* If both allowlist and blocklist are used, check blocklist first then allowlist
* @param {Array|string} blockedIPs
* @param {Array|string} allowedUrls
* @param {string} configuredUrls
* @returns {boolean} true if the configuredUrl is valid
*/
function isValidConfig(blockedIPs, allowedUrls, configuredUrl) {
if (blockedIPs.length === 0) {
if (!allowedUrls.includes(configuredUrl)) return false;
} else if (allowedUrls.length === 0) {
if (exports.isBlockedURL(configuredUrl, blockedIPs)) return false;
} else {
if (exports.isBlockedURL(configuredUrl, blockedIPs) || !allowedUrls.includes(configuredUrl))
return false;
}
return true;
}
export { getIpAddress, isBlockedURL, isValidConfig };
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import * as helper from './graphite_helper';

describe('graphite_helper', function () {
it('valid Url should not be blocked and isBlockedURL should return false', function () {
expect(helper.isBlockedURL('https://www.opensearch.org', ['127.0.0.0/8'])).toEqual(false);
});

it('blocked Url should be blocked and isBlockedURL should return true', function () {
expect(helper.isBlockedURL('https://127.0.0.1', ['127.0.0.0/8'])).toEqual(true);
});

it('invalid Url should be blocked and isBlockedURL should return true', function () {
expect(helper.isBlockedURL('www.opensearch.org', ['127.0.0.0/8'])).toEqual(true);
});

it('blocklist should be checked if blocklist is enabled', function () {
jest.spyOn(helper, 'isBlockedURL').mockReturnValueOnce(false);
helper.isValidConfig(['127.0.0.0/8'], [], 'https://www.opensearch.org');
expect(helper.isBlockedURL).toBeCalled();
});

it('blocklist should be checked it both allowlist and blocklist are enabled', function () {
jest.spyOn(helper, 'isBlockedURL').mockReturnValueOnce(false);
helper.isValidConfig(
['127.0.0.0/8'],
['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
'https://www.opensearch.org'
);
expect(helper.isBlockedURL).toBeCalled();
});

it('with only allowlist, isValidConfig should return false for Url not in the allowlist', function () {
expect(
helper.isValidConfig(
[],
['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
'https://www.opensearch.org'
)
).toEqual(false);
});

it('with only allowlist, isValidConfig should return true for Url in the allowlist', function () {
expect(
helper.isValidConfig(
[],
['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
'https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'
)
).toEqual(true);
});

it('with only blocklist, isValidConfig should return false for Url in the blocklist', function () {
expect(helper.isValidConfig(['127.0.0.0/8'], [], 'https://127.0.0.1')).toEqual(false);
});

it('with only blocklist, isValidConfig should return true for Url not in the blocklist', function () {
expect(helper.isValidConfig(['127.0.0.0/8'], [], 'https://www.opensearch.org')).toEqual(true);
});

it('with both blocklist and allowlist, isValidConfig should return false if allowlist check fails', function () {
expect(
helper.isValidConfig(
['127.0.0.0/8'],
['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
'https://www.opensearch.org'
)
).toEqual(false);
});

it('with both blocklist and allowlist, isValidConfig should return false if blocklist check fails', function () {
expect(
helper.isValidConfig(
['127.0.0.0/8'],
['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
'https://127.0.0.1'
)
).toEqual(false);
});

it('with conflict blocklist and allowlist, isValidConfig should return false if blocklist check fails', function () {
expect(
helper.isValidConfig(['127.0.0.0/8'], ['https://127.0.0.1'], 'https://127.0.0.1')
).toEqual(false);
});
});

0 comments on commit ec28338

Please sign in to comment.