From 5b3a2339c967ff247befc5ba72197fa81e2fa0ec Mon Sep 17 00:00:00 2001 From: Dino Chiesa Date: Tue, 9 Apr 2024 23:52:59 -0700 Subject: [PATCH] feat: BN013 plugin to warn on unreferenced resources --- README.md | 1 + .../plugins/BN013-unreferenced-resources.js | 204 ++++++++++++++++++ .../AM-Assign-Variable-ResourceURL.xml | 15 ++ .../AM-Inject-Proxy-Revision-Header.xml | 7 + .../apiproxy/policies/JS-testme.xml | 8 + .../apiproxy/policies/MV-1.xml | 4 + .../apiproxy/policies/MV-2.xml | 5 + .../apiproxy/policies/OAS-Basic.xml | 12 ++ .../apiproxy/policies/PS-Set-Header.xml | 3 + .../apiproxy/policies/RF-Unknown-Request.xml | 24 +++ .../apiproxy/policies/XSL-Basic.xml | 15 ++ .../apiproxy/proxies/endpoint1.xml | 92 ++++++++ .../apiproxy/resources/java/xeger-1.0.jar | Bin 0 -> 3996 bytes .../apiproxy/resources/jsc/URI.js | 10 + .../apiproxy/resources/jsc/sourceFile1.js | 1 + .../apiproxy/resources/jsc/sourceFile2.js | 2 + .../apiproxy/resources/jsc/sourceFile3.js | 1 + .../apiproxy/resources/oas/example1.yaml | 35 +++ .../apiproxy/resources/oas/example2.yaml | 35 +++ .../resources/properties/settings.properties | 3 + .../apiproxy/resources/py/setHeader1.py | 2 + .../apiproxy/resources/py/setHeader2.py | 2 + .../apiproxy/resources/py/setHeader3.py | 2 + .../apiproxy/resources/stray_file.txt | 1 + .../apiproxy/resources/wsdl/example1.wsdl | 94 ++++++++ .../apiproxy/resources/wsdl/example2.wsdl | 94 ++++++++ .../apiproxy/resources/wsdl/example3.wsdl | 94 ++++++++ .../apiproxy/resources/xsd/example1.xsd | 44 ++++ .../apiproxy/resources/xsd/example2.xsd | 44 ++++ .../apiproxy/resources/xsl/sheet1.xsl | 29 +++ .../apiproxy/resources/xsl/sheet2.xsl | 29 +++ .../apiproxy/resources/xsl/sheet3.xsl | 29 +++ .../apiproxy/targets/target-1.xml | 26 +++ .../apiproxy/unreferenced-resources.xml | 18 ++ test/specs/BN001-incorrectResourceTest.js | 30 ++- test/specs/BN001-xsdResourceTest.js | 67 +++--- test/specs/BN013-unreferenced-resources.js | 90 ++++++++ 37 files changed, 1138 insertions(+), 34 deletions(-) create mode 100644 lib/package/plugins/BN013-unreferenced-resources.js create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Assign-Variable-ResourceURL.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Inject-Proxy-Revision-Header.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/JS-testme.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-1.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-2.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/OAS-Basic.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/PS-Set-Header.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/RF-Unknown-Request.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/XSL-Basic.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/proxies/endpoint1.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/java/xeger-1.0.jar create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/URI.js create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile1.js create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile2.js create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile3.js create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example1.yaml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example2.yaml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/properties/settings.properties create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader1.py create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader2.py create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader3.py create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/stray_file.txt create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example1.wsdl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example2.wsdl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example3.wsdl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example1.xsd create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example2.xsd create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet1.xsl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet2.xsl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet3.xsl create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/targets/target-1.xml create mode 100644 test/fixtures/resources/BN013-unreferenced-resources/apiproxy/unreferenced-resources.xml create mode 100644 test/specs/BN013-unreferenced-resources.js diff --git a/README.md b/README.md index ff55c3bf..c0b88dfa 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ This is the current list: |   |:white_check_mark:| BN009 | Statistics Collector - duplicate policies | Warn on duplicate policies when no conditions are present or conditions are duplicates. | |   |:white_check_mark:| BN010 | Missing policies | Issue an error if a referenced policy is not present in the bundle. | |   |:white_check_mark:| BN011 | Check each XML file for well-formedness.| +|   |:white_check_mark:| BN013 | Unreferenced resources. | Warn for resources that not referenced in any policy. Unreferenced resources are dead code. | | Proxy Definition |   |   |   |   | |   |:white_check_mark:| PD001 | RouteRules to Targets | RouteRules should map to defined Targets. | |   |:white_check_mark:| PD002 | Unreachable Route Rules - defaults | Only one RouteRule should be present without a condition. | diff --git a/lib/package/plugins/BN013-unreferenced-resources.js b/lib/package/plugins/BN013-unreferenced-resources.js new file mode 100644 index 00000000..6a235396 --- /dev/null +++ b/lib/package/plugins/BN013-unreferenced-resources.js @@ -0,0 +1,204 @@ +/* + Copyright 2019-2024 Google LLC + + Licensed 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 + + https://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. +*/ + +const ruleId = require("../myUtil.js").getRuleId(), + util = require("util"), + xpath = require("xpath"), + debug = require("debug")("apigeelint:" + ruleId); + +const plugin = { + ruleId, + name: "Check for unreferenced resources", + message: + "Unreferenced resources are dead code; should be removed from bundles.", + fatal: false, + severity: 1, //1=warn, 2=error + nodeType: "Bundle", + enabled: true +}; + +const validResourceTypesForBoth = [ + "jsc", + "java", + "py", + "xsl", + "wsdl", + "xsd", + "oas" +]; + +const profileMappings = { + apigeex: ["properties", "graphql"], + apigee: ["node", "hosted", "template"] +}; + +const onBundle = function (bundle, cb) { + const validResourceTypes = []; + validResourceTypes.push.apply(validResourceTypes, validResourceTypesForBoth); + validResourceTypes.push.apply( + validResourceTypes, + profileMappings[bundle.profile || "apigee"] + ); + + debug(`validResourceTypes: ${util.format(validResourceTypes)}`); + + const resources = bundle.getResources(), + policies = bundle.getPolicies(); + + debug(`resources: ${util.format(resources)}`); + let flagged = false; + if (resources.length) { + resources.forEach((resource, rix) => { + debug(`resource(${rix}): ${resource.path} ${resource.fname}`); + const marker = "apiproxy/resources/", + ix = resource.path.lastIndexOf(marker); + if (ix < 0) { + return; + } + const trailingPath = resource.path.substr(ix + marker.length), + [rtype, rshortname] = trailingPath.split("/", 2); + debug( + `trailingPath(${trailingPath}) rtype(${rtype}) rshortname(${rshortname})` + ); + if (!rshortname) { + debug(`no resource shortname.`); + return; + } + if (!validResourceTypes.includes(rtype)) { + debug(`${rtype} is not a valid resource type, punt.`); + return; + } + + let found = false; + if (rtype == "java") { + /* + * Handle Java resources specially. + * + * We cannot tell by static analysis at this level if a specific Java + * jar will be used by any particular JavaCallout policy. But we can + * tell if there is no JavaCallout policy, in which case we can be + * sure that every jar is unreferenced. + **/ + found = policies.find((policy) => policy.getType() == "JavaCallout"); + } else if (rtype == "properties") { + /* + * Punt on properties resources. + * + * The runtime sets variables with the contents of the + * properties file. We cannot tell if these variables are used + * by Java callouts, JS, or even sharedflows. + **/ + found = true; + } else { + found = policies.find((policy, _pix) => { + const ptype = policy.getType(); + /* + * Any text resource can be included in an AssignMessage/AssignVariable + * or RaiseFault/FaultResponse/AssignVariable . + **/ + if (ptype == "AssignMessage" || ptype == "RaiseFault") { + const avpath = + ptype == "AssignMessage" + ? "AssignMessage" + : "RaiseFault/FaultResponse"; + const rsrcUrls = xpath.select( + `/${avpath}/AssignVariable/ResourceURL/text()`, + policy.getElement() + ); + debug(`rsrcUrl: ${util.format(rsrcUrls)}`); + + // check any/all + return rsrcUrls.find( + (rsrcUrl) => rsrcUrl.data == `${rtype}://${rshortname}` + ); + } + + // now do checks for specific resource types + const rtypeMappings = { + xsl: { xpath: "ResourceURL/text()", ptype: "XSL" }, + oas: { + xpath: "OASResource/text()", + ptype: "OASValidation" + }, + py: { xpath: `ResourceURL/text()`, ptype: "Script" }, + xsd: { + xpath: "ResourceURL/text()", + ptype: "MessageValidation" + }, + wsdl: { + xpath: "ResourceURL/text()", + ptype: "MessageValidation" + }, + graphql: { + xpath: "ResourceURL/text()", + ptype: "GraphQL" + } + }; + const findit = (path1) => { + const rsrcUrls = xpath.select(path1, policy.getElement()); + // check the first one. there should be only one. + return ( + rsrcUrls && + rsrcUrls.length && + rsrcUrls[0].data == `${rtype}://${rshortname}` + ); + }; + if (rtypeMappings[rtype]) { + return ( + ptype == rtypeMappings[rtype].ptype && + findit(`/${ptype}/${rtypeMappings[rtype].xpath}`) + ); + } + + // handle jsc specially, because of IncludeURL + if (rtype == "jsc" && ptype == "Javascript") { + const rsrcUrls = xpath.select( + `/Javascript/IncludeURL/text()`, + policy.getElement() + ); + // check any/all + if ( + rsrcUrls.find((rsrcUrl) => rsrcUrl.data == `jsc://${rshortname}`) + ) { + return true; + } + return findit(`/Javascript/ResourceURL/text()`); + } + + return false; // not found + }); + } + + if (!found) { + flagged = true; + debug(`unreferenced: ${resource.fname}`); + bundle.addMessage({ + plugin, + entity: resource, + message: `Unreferenced resource ${rtype}/${resource.fname}. There are no policies that reference this resource.` + }); + } + }); + } + if (typeof cb == "function") { + cb(null, flagged); + } +}; + +module.exports = { + plugin, + onBundle +}; diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Assign-Variable-ResourceURL.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Assign-Variable-ResourceURL.xml new file mode 100644 index 00000000..27fa2976 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Assign-Variable-ResourceURL.xml @@ -0,0 +1,15 @@ + + false + + xslContents + xsl://sheet3.xsl + + + jsContents + jsc://sourceFile1.js + + + wsdlContents + wsdl://example2.wsdl + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Inject-Proxy-Revision-Header.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Inject-Proxy-Revision-Header.xml new file mode 100644 index 00000000..fcf6c0ae --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/AM-Inject-Proxy-Revision-Header.xml @@ -0,0 +1,7 @@ + + + +
{apiproxy.name} r{apiproxy.revision}
+
+
+
diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/JS-testme.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/JS-testme.xml new file mode 100644 index 00000000..89950cb2 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/JS-testme.xml @@ -0,0 +1,8 @@ + + + + value-here + + jsc://URI.js + jsc://sourceFile2.js + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-1.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-1.xml new file mode 100644 index 00000000..84ec120a --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-1.xml @@ -0,0 +1,4 @@ + + message + xsd://example1.xsd + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-2.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-2.xml new file mode 100644 index 00000000..b72c86eb --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/MV-2.xml @@ -0,0 +1,5 @@ + + message + wsdl://example1.wsdl + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/OAS-Basic.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/OAS-Basic.xml new file mode 100644 index 00000000..a5145d3f --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/OAS-Basic.xml @@ -0,0 +1,12 @@ + + oas://example1.yaml + + true + +
false
+ false + false +
+
+ request +
diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/PS-Set-Header.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/PS-Set-Header.xml new file mode 100644 index 00000000..738e2af5 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/PS-Set-Header.xml @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/RF-Unknown-Request.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/RF-Unknown-Request.xml new file mode 100644 index 00000000..889f41bd --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/RF-Unknown-Request.xml @@ -0,0 +1,24 @@ + + true + + + + pyContents + py://setHeader2.py + + + { + "error" : { + "code" : 404.01, + "message" : "that request was unknown; try a different request." + } +} + + 404 + Not Found + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/XSL-Basic.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/XSL-Basic.xml new file mode 100644 index 00000000..368a032c --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/policies/XSL-Basic.xml @@ -0,0 +1,15 @@ + + outboundMessageTemplate + transformedContent + xsl://sheet1.xsl + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/proxies/endpoint1.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/proxies/endpoint1.xml new file mode 100644 index 00000000..da130fec --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/proxies/endpoint1.xml @@ -0,0 +1,92 @@ + + + + /unreferenced-resources + + secure + + + + + + AM-Inject-Proxy-Revision-Header + + true + + + + + + AM-Assign-Variable-ResourceURL + + + MV-1 + + + MV-2 + + + OAS-Basic + + + PS-Set-Header + + + JS-testme + + + XSL-Basic + + + + + + + + + + AM-Inject-Proxy-Revision-Header + + + + + + + + + + + + + + + + + proxy.pathsuffix MatchesPath "/t1" and request.verb = "GET" + + + + + + + + proxy.pathsuffix MatchesPath "/t2" and request.verb = "POST" + + + + + + RF-Unknown-Request + + + + + + + + + + target-1 + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/java/xeger-1.0.jar b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/java/xeger-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..a322dddf9644d09bbd7665da036508f0dc50303d GIT binary patch literal 3996 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 + +/* jshint esversion:9, node:true, strict:implied */ +/* global process, console, Buffer */ + +var c = context.getVariable("my-variable"); diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile1.js b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile1.js new file mode 100644 index 00000000..7bf9451d --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile1.js @@ -0,0 +1 @@ +var c = context.getVariable("my-variable"); diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile2.js b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile2.js new file mode 100644 index 00000000..c58470d1 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile2.js @@ -0,0 +1,2 @@ + +var c = context.getVariable("my-variable-2"); diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile3.js b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile3.js new file mode 100644 index 00000000..7bf9451d --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/jsc/sourceFile3.js @@ -0,0 +1 @@ +var c = context.getVariable("my-variable"); diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example1.yaml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example1.yaml new file mode 100644 index 00000000..b54b6ee3 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example1.yaml @@ -0,0 +1,35 @@ +# Copyright 2022 Google LLC +# Licensed 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. +openapi: 3.0.0 +info: + title: Sample Drupal Developer Portal + description: A portal for an API key protected proxy + version: "1" + contact: + name: Sample Drupal Developer Portal +servers: + - url: https://[APIGEE_HOST]/v1/samples/drupal-developer-portal +security: + - APIKeyQuery: [] +paths: + /: + get: + operationId: GetMessage + responses: + '200': + description: A successful response. + content: {} +components: + securitySchemes: + APIKeyQuery: + type: apiKey + name: apikey + in: query \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example2.yaml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example2.yaml new file mode 100644 index 00000000..b54b6ee3 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/oas/example2.yaml @@ -0,0 +1,35 @@ +# Copyright 2022 Google LLC +# Licensed 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. +openapi: 3.0.0 +info: + title: Sample Drupal Developer Portal + description: A portal for an API key protected proxy + version: "1" + contact: + name: Sample Drupal Developer Portal +servers: + - url: https://[APIGEE_HOST]/v1/samples/drupal-developer-portal +security: + - APIKeyQuery: [] +paths: + /: + get: + operationId: GetMessage + responses: + '200': + description: A successful response. + content: {} +components: + securitySchemes: + APIKeyQuery: + type: apiKey + name: apikey + in: query \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/properties/settings.properties b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/properties/settings.properties new file mode 100644 index 00000000..97db0dc1 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/properties/settings.properties @@ -0,0 +1,3 @@ +apiKeyVar=b0a30d4b1be2 +abdVar=foo +abcVar=bar \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader1.py b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader1.py new file mode 100644 index 00000000..a8d54588 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader1.py @@ -0,0 +1,2 @@ +flow.setVariable("response.header.X-Apigee-Demo-target", flow.getVariable("target.url")); +print 'Reached the script & assigned header variable' diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader2.py b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader2.py new file mode 100644 index 00000000..a8d54588 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader2.py @@ -0,0 +1,2 @@ +flow.setVariable("response.header.X-Apigee-Demo-target", flow.getVariable("target.url")); +print 'Reached the script & assigned header variable' diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader3.py b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader3.py new file mode 100644 index 00000000..a8d54588 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/py/setHeader3.py @@ -0,0 +1,2 @@ +flow.setVariable("response.header.X-Apigee-Demo-target", flow.getVariable("target.url")); +print 'Reached the script & assigned header variable' diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/stray_file.txt b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/stray_file.txt new file mode 100644 index 00000000..ce013625 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/stray_file.txt @@ -0,0 +1 @@ +hello diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example1.wsdl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example1.wsdl new file mode 100644 index 00000000..1b904257 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example1.wsdl @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example2.wsdl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example2.wsdl new file mode 100644 index 00000000..1b904257 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example2.wsdl @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example3.wsdl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example3.wsdl new file mode 100644 index 00000000..1b904257 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/wsdl/example3.wsdl @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example1.xsd b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example1.xsd new file mode 100644 index 00000000..3a316b8b --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example1.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example2.xsd b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example2.xsd new file mode 100644 index 00000000..3a316b8b --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsd/example2.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet1.xsl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet1.xsl new file mode 100644 index 00000000..c307e829 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet1.xsl @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet2.xsl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet2.xsl new file mode 100644 index 00000000..c307e829 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet2.xsl @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet3.xsl b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet3.xsl new file mode 100644 index 00000000..c307e829 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/resources/xsl/sheet3.xsl @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/targets/target-1.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/targets/target-1.xml new file mode 100644 index 00000000..33f92348 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/targets/target-1.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + true + false + + + 1xx,2xx,3xx + User-Agent,Referer,Accept-Language + apikey + + + https://target-1.demo.altostrat.com + + diff --git a/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/unreferenced-resources.xml b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/unreferenced-resources.xml new file mode 100644 index 00000000..6cc80275 --- /dev/null +++ b/test/fixtures/resources/BN013-unreferenced-resources/apiproxy/unreferenced-resources.xml @@ -0,0 +1,18 @@ + + + 1491498008040 + + unreferenced-resources + 1491517153495 + + + default + + + + + + default + + false + diff --git a/test/specs/BN001-incorrectResourceTest.js b/test/specs/BN001-incorrectResourceTest.js index f9e4d1c4..835b1070 100644 --- a/test/specs/BN001-incorrectResourceTest.js +++ b/test/specs/BN001-incorrectResourceTest.js @@ -1,5 +1,5 @@ /* - Copyright 2019-2023 Google LLC + Copyright 2019-2024 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ const assert = require("assert"), path = require("path"), - debug = require("debug")("apigeelint:BN001-test"), + debug = require("debug")("apigeelint:BN001"), bl = require("../../lib/package/bundleLinter.js"); describe(`BN001 - bundle with incorrect resource`, () => { @@ -50,19 +50,29 @@ describe(`BN001 - bundle with incorrect resource`, () => { //console.log("**actualErrors: "+ JSON.stringify(actualErrors)); debug(JSON.stringify(actualErrors, null, 1)); - assert.equal(actualErrors.length, 1); - assert.ok(actualErrors[0].messages.length); - assert.equal(actualErrors[0].messages.length, 1); - assert.ok(actualErrors[0].messages[0].message); + const bn001Items = actualErrors.filter((e) => + e.messages.find((m) => m.ruleId == "BN001") + ); + + assert.equal(bn001Items.length, 1); + assert.ok(bn001Items[0].messages.length); + + // disregard all warnings or errors except those from this plugin + bn001Items[0].messages = bn001Items[0].messages.filter( + (m) => m.ruleId == "BN001" + ); + + assert.equal(bn001Items[0].messages.length, 1); + assert.ok(bn001Items[0].messages[0].message); assert.ok( - actualErrors[0].messages[0].message.startsWith( + bn001Items[0].messages[0].message.startsWith( "Unexpected extension found with file" ), - actualErrors[0].messages[0].message + bn001Items[0].messages[0].message ); assert.ok( - actualErrors[0].messages[0].message.indexOf("invalid_file.js") > 0, - actualErrors[0].messages[0].message + bn001Items[0].messages[0].message.indexOf("invalid_file.js") > 0, + bn001Items[0].messages[0].message ); }); }); diff --git a/test/specs/BN001-xsdResourceTest.js b/test/specs/BN001-xsdResourceTest.js index db5dbe36..751e4731 100644 --- a/test/specs/BN001-xsdResourceTest.js +++ b/test/specs/BN001-xsdResourceTest.js @@ -1,5 +1,5 @@ /* - Copyright 2019-2023 Google LLC + Copyright 2019-2024 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,42 +18,61 @@ /* global describe, it */ const assert = require("assert"), - path = require("path"), - debug = require("debug")("apigeelint:BN001-test"), - bl = require("../../lib/package/bundleLinter.js"); + path = require("path"), + debug = require("debug")("apigeelint:BN001"), + bl = require("../../lib/package/bundleLinter.js"); describe(`BN001 - bundle with XSD resource`, () => { - it('should generate the expected errors', () => { + it("should generate the expected errors", () => { const configuration = { - debug: true, - source: { - type: "filesystem", - path: path.resolve(__dirname, '../fixtures/resources/BN001-xsd-resources/apiproxy'), - bundleType: "apiproxy" - }, - profile: 'apigee', - excluded: {}, - setExitCode: false, - output: () => {} // suppress output - }; + debug: true, + source: { + type: "filesystem", + path: path.resolve( + __dirname, + "../fixtures/resources/BN001-xsd-resources/apiproxy" + ), + bundleType: "apiproxy" + }, + profile: "apigee", + excluded: {}, + setExitCode: false, + output: () => {} // suppress output + }; bl.lint(configuration, (bundle) => { const items = bundle.getReport(); assert.ok(items); assert.ok(items.length); - const actualErrors = items.filter(item => item.messages && item.messages.length); + const actualErrors = items.filter( + (item) => item.messages && item.messages.length + ); debug(JSON.stringify(actualErrors, null, 2)); assert.equal(actualErrors.length, 1); assert.ok(actualErrors[0].messages.length); - assert.equal(actualErrors[0].messages.length, 1); - assert.ok(actualErrors[0].messages[0].message); - assert.ok(actualErrors[0].messages[0].message.startsWith('Unexpected extension found with file'), - actualErrors[0].messages[0].message); - assert.ok(actualErrors[0].messages[0].message.indexOf("set1.properties") > 0, - actualErrors[0].messages[0].message); + const bn001Items = actualErrors.filter((e) => + e.messages.find((m) => m.ruleId == "BN001") + ); + + // disregard all warnings or errors except those from this plugin + bn001Items[0].messages = bn001Items[0].messages.filter( + (m) => m.ruleId == "BN001" + ); + + assert.equal(bn001Items[0].messages.length, 1); + assert.ok(bn001Items[0].messages[0].message); + assert.ok( + bn001Items[0].messages[0].message.startsWith( + "Unexpected extension found with file" + ), + bn001Items[0].messages[0].message + ); + assert.ok( + bn001Items[0].messages[0].message.indexOf("set1.properties") > 0, + bn001Items[0].messages[0].message + ); }); }); - }); diff --git a/test/specs/BN013-unreferenced-resources.js b/test/specs/BN013-unreferenced-resources.js new file mode 100644 index 00000000..6996fb11 --- /dev/null +++ b/test/specs/BN013-unreferenced-resources.js @@ -0,0 +1,90 @@ +/* + Copyright 2019-2024 Google LLC + + Licensed 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 + + https://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. +*/ +/* global configuration, describe, it */ +const assert = require("assert"), + path = require("path"), + util = require("util"), + testID = "BN013", + debug = require("debug")("apigeelint:" + testID), + //Bundle = require("../../lib/package/Bundle.js"), + bl = require("../../lib/package/bundleLinter.js"); + +describe("BN013 - Check for unreferenced resources", function () { + it("should flag unused resources in the test bundle", () => { + const configuration = { + debug: true, + source: { + type: "filesystem", + path: path.resolve( + __dirname, + "../fixtures/resources/BN013-unreferenced-resources/apiproxy" + ), + bundleType: "apiproxy" + }, + profile: "apigeex", + excluded: {}, + setExitCode: false, + output: () => {} // suppress output + }; + + debug("test configuration: " + JSON.stringify(configuration)); + bl.lint(configuration, (bundle) => { + const items = bundle.getReport(); + assert.ok(items); + assert.ok(items.length); + const actualErrors = items.filter( + (item) => item.messages && item.messages.length + ); + assert.ok(actualErrors.length); + debug(util.format(actualErrors)); + debug("First error: " + util.format(actualErrors[0])); + + const bn013Items = actualErrors.filter((e) => + e.messages.find((m) => m.ruleId == "BN013") + ); + + debug("BN013 items: " + util.format(bn013Items)); + assert.equal(bn013Items.length, 1); + debug(util.format(bn013Items[0])); + assert.ok(bn013Items[0].messages); + + // disregard all warnings or errors except those from this plugin + bn013Items[0].messages = bn013Items[0].messages.filter( + (m) => m.ruleId == "BN013" + ); + + const expected = [ + "xsl/sheet2.xsl", + "jsc/sourceFile3.js", + "wsdl/example3.wsdl", + "py/setHeader3.py", + "xsd/example2.xsd", + "oas/example2.yaml", + "java/xeger-1.0.jar" + ]; + assert.equal(bn013Items[0].messages.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assert.ok( + expected.find( + (rsrc) => + bn013Items[0].messages[i].message == + `Unreferenced resource ${rsrc}. There are no policies that reference this resource.` + ) + ); + } + }); + }); +});