From ce52c9bd56fabb2259ce96ab79eae0fb686e66fa Mon Sep 17 00:00:00 2001 From: aignachk Date: Thu, 28 Jan 2021 12:07:38 -0500 Subject: [PATCH 1/6] SECKSD-8209 added cli commands to add/update pragma header settings --- README.md | 3 +- bin/commands/pragmaheader.js | 43 +++++++++++++++++++++++++++ bin/commands/pragmaheader.modify.js | 46 +++++++++++++++++++++++++++++ src/advancedsettings.js | 25 ++++++++++++++++ src/constants.js | 1 + templates/pragma-header.json | 19 ++++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 bin/commands/pragmaheader.js create mode 100644 bin/commands/pragmaheader.modify.js create mode 100644 templates/pragma-header.json diff --git a/README.md b/README.md index 3c365c2..2c4b12b 100755 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ Commands: disable-override-http-header-logging (Beta) Disable the HTTP Header Logging Override settings. prefetch-requests (Beta) Display the Prefetch Requests settings. modify-prefetch-requests (Beta) Update the Prefetch Requests settings. + pragma-header (Beta) Display Pragma Header settings. create-config (Beta) Create a new security config. delete-config (Beta) Delete a security config. create-security-policy (Beta) Create a security policy. @@ -210,4 +211,4 @@ The Akamai CLI is a new tool and as such we have made some design choices worth * Credentials - the tool looks for credentials in the 'appsec' section in your ~/.edgerc file. If not present, it will look for the section 'default'. Alternatively you can provide the section name using the --section option in every command. If you are unfamiliar with the authentication and provisioning for OPEN APIs, see the "Get Started" section of https://developer.akamai.com ## References -1A configuration version is editable if it is not active currently or in the past in any of the environments(staging or production). \ No newline at end of file +1A configuration version is editable if it is not active currently or in the past in any of the environments(staging or production). diff --git a/bin/commands/pragmaheader.js b/bin/commands/pragmaheader.js new file mode 100644 index 0000000..e7d510e --- /dev/null +++ b/bin/commands/pragmaheader.js @@ -0,0 +1,43 @@ +let AdvancedSettings = require('../../src/advancedsettings').advancedsettings; +let out = require('./lib/out'); + +class PragmaHeaderCommand { + constructor() { + this.flags = 'pragma-header'; + this.desc = '(Beta) Display Pragma Header settings'; + this.setup = this.setup.bind(this); + this.run = this.run.bind(this); + } + + setup(sywac) { + sywac + .number('--config ', { + desc: 'Configuration ID. Mandatory if you have more than one configuration.', + group: 'Optional:', + required: false + }) + .string('--version ', { + desc: + "Version Number. It can also take the values 'PROD' or 'PRODUCTION' or 'STAGING'. If not provided, latest version is assumed.", + group: 'Optional:', + required: false + }) + .string('--policy ', { + desc: + 'Policy ID. If not provided, we try to use the policy available on file. If you have more than one policy, this option must be provided.', + group: 'Optional:', + required: false + }); + } + run(options) { + out.print({ + promise: new AdvancedSettings(options).getPragmaHeader(), + args: options, + success: (args, data) => { + return JSON.stringify(data); + } + }); + } +} + +module.exports = new PragmaHeaderCommand (); diff --git a/bin/commands/pragmaheader.modify.js b/bin/commands/pragmaheader.modify.js new file mode 100644 index 0000000..e2cacda --- /dev/null +++ b/bin/commands/pragmaheader.modify.js @@ -0,0 +1,46 @@ +let AdvancedSettings = require('../../src/advancedsettings').advancedsettings; +let out = require('./lib/out'); + +class PragmaHeaderModifyCommand { + constructor() { + this.flags = 'modify-pragma-header'; + this.desc = '(Beta) Update Pragma Header settings.'; + this.setup = this.setup.bind(this); + this.run = this.run.bind(this); + } + + setup(sywac) { + sywac + .positional('<@path>', { + paramsDesc: 'The input file path.' + }) + .number('--config ', { + desc: 'Configuration ID. Mandatory if you have more than one configuration.', + group: 'Optional:', + required: false + }) + .string('--version ', { + desc: + "Version Number. It can also take the values 'PROD' or 'PRODUCTION' or 'STAGING'. If not provided, latest version is assumed.", + group: 'Optional:', + required: false + }) + .check((argv, context) => { + if (!argv['@path'].startsWith('@')) { + return context.cliMessage("ERROR: Invalid file name, should start with '@'"); + } + }); + } + run(options) { + options.file = options['@path'].replace('@', ''); + out.print({ + promise: new AdvancedSettings(options).updatePragmaHeader(), + args: options, + success: (args, data) => { + return JSON.stringify(data); + } + }); + } +} + +module.exports = new PragmaHeaderModifyCommand(); diff --git a/src/advancedsettings.js b/src/advancedsettings.js index a6cdd2b..df9f5b3 100644 --- a/src/advancedsettings.js +++ b/src/advancedsettings.js @@ -104,6 +104,31 @@ class AdvancedSettings { throw `The file does not exists: ${this._options['file']}`; } } + + getPragmaHeader() { + if (this._options.policy) { + return this._policyProvider.policyId().then(policyId => { + return this._version.readResource(URIs.PRAGMA_HEADER, [policyId]); + }); + } + return this._version.readResource(URIs.PRAGMA_HEADER, []); + } + + updatePragmaHeader() { + if (fs.existsSync(this._options['file'])) { + let payload = fs.readFileSync(untildify(this._options['file']), 'utf8'); + let data; + try { + data = JSON.parse(payload); + } catch (err) { + throw 'The input JSON is not valid'; + } + return this._version.updateResource(URIs.PRAGMA_HEADER, [], data); + } else { + throw `The file does not exists: ${this._options['file']}`; + } + } + } module.exports = { diff --git a/src/constants.js b/src/constants.js index f2ee427..4ed3dcf 100644 --- a/src/constants.js +++ b/src/constants.js @@ -81,6 +81,7 @@ const resources = { BYPASS_NETWORK_LIST: '/appsec/v1/configs/%s/versions/%s/bypass-network-lists', HTTP_HEADER_LOGGING: '/appsec/v1/configs/%s/versions/%s/advanced-settings/logging', PREFETCH: '/appsec/v1/configs/%s/versions/%s/advanced-settings/prefetch', + PRAGMA_HEADER: '/appsec/v1/configs/%s/versions/%s/advanced-settings/pragma-header', SECURITY_POLICY_HTTP_HEADER_LOGGING: '/appsec/v1/configs/%s/versions/%s/security-policies/%s/advanced-settings/logging', VERSION_NOTES: '/appsec/v1/configs/%s/versions/%s/version-notes', diff --git a/templates/pragma-header.json b/templates/pragma-header.json new file mode 100644 index 0000000..b4292f5 --- /dev/null +++ b/templates/pragma-header.json @@ -0,0 +1,19 @@ +{ + "action": "REMOVE", + "excludeCondition": [ + { + "type":"requestHeaderValueMatch", + "positiveMatch": true, + "header": "accept", + "value": [ "application/json", "application/xml"], + "valueCase": false, + "valueWildcard": true + }, + { + "type":"ipMatch", + "positiveMatch": true, + "value": [ "1.1.1.1", "192.168.100.14/24"], + "useHeaders": false + } + ] +} From 43e3f3a4966653d7e9ca42e756602516a94eba4b Mon Sep 17 00:00:00 2001 From: aignachk Date: Thu, 28 Jan 2021 12:10:06 -0500 Subject: [PATCH 2/6] SECKSD-8209 added cli commands to add/update pragma header settings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2c4b12b..c3abb05 100755 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Commands: prefetch-requests (Beta) Display the Prefetch Requests settings. modify-prefetch-requests (Beta) Update the Prefetch Requests settings. pragma-header (Beta) Display Pragma Header settings. + modify-pragma-header (Beta) Update Pragma Header settings. create-config (Beta) Create a new security config. delete-config (Beta) Delete a security config. create-security-policy (Beta) Create a security policy. From a8ba92b0523c7f5935cc16ee6eac8d9e9d2281f5 Mon Sep 17 00:00:00 2001 From: aignachk Date: Mon, 1 Feb 2021 13:56:24 -0500 Subject: [PATCH 3/6] SECKSD-8215 policy level pragma header --- src/advancedsettings.js | 7 ++++++- src/constants.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/advancedsettings.js b/src/advancedsettings.js index df9f5b3..08b303a 100644 --- a/src/advancedsettings.js +++ b/src/advancedsettings.js @@ -108,7 +108,7 @@ class AdvancedSettings { getPragmaHeader() { if (this._options.policy) { return this._policyProvider.policyId().then(policyId => { - return this._version.readResource(URIs.PRAGMA_HEADER, [policyId]); + return this._version.readResource(URIs.SECURITY_POLICY_PRAGMA_HEADER, [policyId]); }); } return this._version.readResource(URIs.PRAGMA_HEADER, []); @@ -123,6 +123,11 @@ class AdvancedSettings { } catch (err) { throw 'The input JSON is not valid'; } + if (this._options.policy) { + return this._policyProvider.policyId().then(policyId => { + return this._version.updateResource(URIs.SECURITY_POLICY_PRAGMA_HEADER, [policyId], data); + }); + } return this._version.updateResource(URIs.PRAGMA_HEADER, [], data); } else { throw `The file does not exists: ${this._options['file']}`; diff --git a/src/constants.js b/src/constants.js index 4ed3dcf..765a4b8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -82,6 +82,7 @@ const resources = { HTTP_HEADER_LOGGING: '/appsec/v1/configs/%s/versions/%s/advanced-settings/logging', PREFETCH: '/appsec/v1/configs/%s/versions/%s/advanced-settings/prefetch', PRAGMA_HEADER: '/appsec/v1/configs/%s/versions/%s/advanced-settings/pragma-header', + SECURITY_POLICY_PRAGMA_HEADER:'/appsec/v1/configs/%s/versions/%s/security-policies/%s/advanced-settings/pragma-header', SECURITY_POLICY_HTTP_HEADER_LOGGING: '/appsec/v1/configs/%s/versions/%s/security-policies/%s/advanced-settings/logging', VERSION_NOTES: '/appsec/v1/configs/%s/versions/%s/version-notes', From 55d970b5c0c87c1e146d585b352197c668f4e540 Mon Sep 17 00:00:00 2001 From: Siva Ghantasala Date: Mon, 1 Feb 2021 18:58:37 -0500 Subject: [PATCH 4/6] SECKSD-7888 - Remove host/api changes for modify match target. --- bin/commands/apimatchtarget.modify.js | 4 +- bin/commands/matchtarget.modify.js | 8 +++- src/matchtarget.js | 55 +++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/bin/commands/apimatchtarget.modify.js b/bin/commands/apimatchtarget.modify.js index f57fd2e..5d78e09 100644 --- a/bin/commands/apimatchtarget.modify.js +++ b/bin/commands/apimatchtarget.modify.js @@ -2,7 +2,7 @@ let out = require('./lib/out'); let MatchTarget = require('../../src/matchtarget').matchTarget; let logger = require('../../src/constants').logger('modify-api-match-target'); -const SUB_CPMMANDS = ['add-api']; +const SUB_CPMMANDS = ['add-api', 'remove-api']; class ModifyAPIMatchTargetCommand { constructor() { this.flags = 'modify-api-match-target'; @@ -55,6 +55,8 @@ class ModifyAPIMatchTargetCommand { switch (options.subcommand) { case 'add-api': return new MatchTarget(options).addApi(); + case 'remove-api': + return new MatchTarget(options).removeApi(); default: return null; } diff --git a/bin/commands/matchtarget.modify.js b/bin/commands/matchtarget.modify.js index a680894..2fbbd75 100644 --- a/bin/commands/matchtarget.modify.js +++ b/bin/commands/matchtarget.modify.js @@ -2,7 +2,7 @@ let out = require('./lib/out'); let MatchTarget = require('../../src/matchtarget').matchTarget; let logger = require('../../src/constants').logger('modify-match-target'); -const SUB_CPMMANDS = ['add-hostname']; +const SUB_CPMMANDS = ['add-hostname', 'remove-hostname']; class ModifyMatchTargetCommand { constructor() { this.flags = 'modify-match-target'; @@ -42,7 +42,9 @@ class ModifyMatchTargetCommand { run(options) { logger.debug(JSON.stringify(options)); - options.hostnames = [options.hostname]; + if (options.hostname) { + options.hostnames = [options.hostname]; + } out.print({ promise: this._getOperation(options), args: options, @@ -56,6 +58,8 @@ class ModifyMatchTargetCommand { switch (options.subcommand) { case 'add-hostname': return new MatchTarget(options).addHostnames(); + case 'remove-hostname': + return new MatchTarget(options).removeHostname(); default: return null; } diff --git a/src/matchtarget.js b/src/matchtarget.js index 2165877..c140b8b 100644 --- a/src/matchtarget.js +++ b/src/matchtarget.js @@ -91,6 +91,34 @@ class MatchTarget { }); } + removeHostname() { + return this._version + .readResource(URIs.MATCH_TARGET, [this._options['match-target']]) + .then(matchTarget => { + if (!matchTarget.hostnames) { + matchTarget.hostnames = []; + } + let hostExists = false; + for (var i = 0; i < matchTarget.hostnames.length; i++) { + if (matchTarget.hostnames[i] === this._options.hostname) { + matchTarget.hostnames.splice(i, 1); + hostExists = true; + } + } + if (!hostExists) { + throw 'The specified hostname not present in the match target'; + } + + delete matchTarget.validations; + logger.debug('Updated match target: %s', JSON.stringify(matchTarget)); + return this._version.updateResource( + URIs.MATCH_TARGET, + [this._options['match-target']], + matchTarget + ); + }); + } + addApi() { return this._version .readResource(URIs.MATCH_TARGET, [this._options['match-target']]) @@ -117,6 +145,33 @@ class MatchTarget { }); } + removeApi() { + return this._version + .readResource(URIs.MATCH_TARGET, [this._options['match-target']]) + .then(matchTarget => { + if (!matchTarget.apis) { + matchTarget.apis = []; + } + let apiExists = false; + for (let i = 0; i < matchTarget.apis.length; i++) { + if (matchTarget.apis[i].id == this._options.api) { + matchTarget.apis.splice(i, 1); + apiExists = true; + } + } + if (!apiExists) { + throw 'The specified api not present in the match target'; + } + delete matchTarget.validations; + logger.debug('Updated match target: %s', JSON.stringify(matchTarget)); + return this._version.updateResource( + URIs.MATCH_TARGET, + [this._options['match-target']], + matchTarget + ); + }); + } + _updateOrder(targetIdsInOrder) { let targetSequence = []; for (let i = 0; i < targetIdsInOrder.length; i++) { From e54afa0c1dad79091eb86d63e0e9fafb588f6f73 Mon Sep 17 00:00:00 2001 From: Siva Ghantasala Date: Tue, 2 Feb 2021 11:51:56 -0500 Subject: [PATCH 5/6] SECKSD-7888 - Incorporating code review comments --- src/matchtarget.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/matchtarget.js b/src/matchtarget.js index c140b8b..a9c39b2 100644 --- a/src/matchtarget.js +++ b/src/matchtarget.js @@ -95,14 +95,13 @@ class MatchTarget { return this._version .readResource(URIs.MATCH_TARGET, [this._options['match-target']]) .then(matchTarget => { - if (!matchTarget.hostnames) { - matchTarget.hostnames = []; - } + matchTarget.hostnames = matchTarget.hostnames || []; let hostExists = false; - for (var i = 0; i < matchTarget.hostnames.length; i++) { + for (let i = 0; i < matchTarget.hostnames.length; i++) { if (matchTarget.hostnames[i] === this._options.hostname) { matchTarget.hostnames.splice(i, 1); hostExists = true; + break; } } if (!hostExists) { @@ -130,6 +129,7 @@ class MatchTarget { for (let i = 0; i < matchTarget.apis.length; i++) { if (matchTarget.apis[i].id == this._options.api) { apiExists = true; + break; } } if (!apiExists) { @@ -149,14 +149,13 @@ class MatchTarget { return this._version .readResource(URIs.MATCH_TARGET, [this._options['match-target']]) .then(matchTarget => { - if (!matchTarget.apis) { - matchTarget.apis = []; - } + matchTarget.apis = matchTarget.apis || []; let apiExists = false; for (let i = 0; i < matchTarget.apis.length; i++) { if (matchTarget.apis[i].id == this._options.api) { matchTarget.apis.splice(i, 1); apiExists = true; + break; } } if (!apiExists) { From dacd43a85fe8fda35d426ce9e2b522c1b9f1b805 Mon Sep 17 00:00:00 2001 From: Shubham Kabra Date: Wed, 17 Feb 2021 14:26:31 -0500 Subject: [PATCH 6/6] MINOR: Update v2.1.0 --- .npmrc | 4 ++++ cli.json | 2 +- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..71c5f90 --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +registry=https://registry.npmjs.org/ +fetch-retry-mintimeout=1000 +fetch-retry-maxtimeout=5000 +strict-ssl=false \ No newline at end of file diff --git a/cli.json b/cli.json index 4e8baae..f64c132 100644 --- a/cli.json +++ b/cli.json @@ -5,7 +5,7 @@ "commands": [ { "name": "appsec", - "version": "2.0.0", + "version": "2.1.0", "description": "Akamai Security tools for protecting websites." } ] diff --git a/package-lock.json b/package-lock.json index f197869..d683764 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "akamaicliappsec", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 664a10c..57366c8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "akamaicliappsec", - "version": "2.0.0", + "version": "2.1.0", "description": "A wrapping development kit to interface common tasks with akamai's Security {OPEN} API.", "repository": "https://github.com/akamai/cli-appsec", "license": "Apache-2.0",