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/README.md b/README.md
index 3c365c2..c3abb05 100755
--- a/README.md
+++ b/README.md
@@ -129,6 +129,8 @@ 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.
+ 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.
@@ -210,4 +212,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/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/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/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",
diff --git a/src/advancedsettings.js b/src/advancedsettings.js
index a6cdd2b..08b303a 100644
--- a/src/advancedsettings.js
+++ b/src/advancedsettings.js
@@ -104,6 +104,36 @@ 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.SECURITY_POLICY_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';
+ }
+ 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']}`;
+ }
+ }
+
}
module.exports = {
diff --git a/src/constants.js b/src/constants.js
index f2ee427..765a4b8 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -81,6 +81,8 @@ 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_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',
diff --git a/src/matchtarget.js b/src/matchtarget.js
index 2165877..a9c39b2 100644
--- a/src/matchtarget.js
+++ b/src/matchtarget.js
@@ -91,6 +91,33 @@ class MatchTarget {
});
}
+ removeHostname() {
+ return this._version
+ .readResource(URIs.MATCH_TARGET, [this._options['match-target']])
+ .then(matchTarget => {
+ matchTarget.hostnames = matchTarget.hostnames || [];
+ let hostExists = false;
+ 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) {
+ 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']])
@@ -102,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) {
@@ -117,6 +145,32 @@ class MatchTarget {
});
}
+ removeApi() {
+ return this._version
+ .readResource(URIs.MATCH_TARGET, [this._options['match-target']])
+ .then(matchTarget => {
+ 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) {
+ 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++) {
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
+ }
+ ]
+}