diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 666f2a5b1f..9a902c3348 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -488,6 +488,7 @@ jobs: - sslyze - test-scan - trivy + - whatweb - typo3scan - wpscan - zap @@ -563,6 +564,7 @@ jobs: - kubeaudit - ncrack - nmap + - whatweb steps: - name: Checkout uses: actions/checkout@v2 @@ -1052,6 +1054,19 @@ jobs: cd tests/integration/ npx jest --ci --color scanner/sslyze.test.js + # ---- Whatweb Integration Tests ---- + + - name: "Whatweb Integration Tests" + run: | + kubectl -n integration-tests delete scans --all + helm -n integration-tests install whatweb ./scanners/whatweb/ \ + --set="parser.image.tag=sha-$(git rev-parse --short HEAD)" \ + --set="parser.image.repository=docker.io/${{ env.DOCKER_NAMESPACE }}/parser-whatweb" + --set="parser.env[0].name=CRASH_ON_FAILED_VALIDATION" \ + --set-string="parser.env[0].value=true" + cd tests/integration/ + npx jest --ci --color scanner/whatweb.test.js + # ---- WPScan Integration Tests ---- - name: "WPScan Integration Tests" diff --git a/scanners/whatweb/.helm-docs.gotmpl b/scanners/whatweb/.helm-docs.gotmpl new file mode 100644 index 0000000000..d3c1316c50 --- /dev/null +++ b/scanners/whatweb/.helm-docs.gotmpl @@ -0,0 +1,32 @@ +{{- /* +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 +*/ -}} + +{{- define "extra.docsSection" -}} +--- +title: "Whatweb" +category: "scanner" +type: "Network" +state: "released" +appVersion: "{{ template "chart.appVersion" . }}" +usecase: "Website identification" +--- + +![Whatweb logo](https://morningstarsecurity.com/wp-content/uploads/2019/02/WhatWeb-Logo-800px.png) + +{{- end }} + +{{- define "extra.dockerDeploymentSection" -}} +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `{{ template "chart.appVersion" . }}` +{{- end }} + +{{- define "extra.chartAboutSection" -}} +## What is WHATWEB? +Whatweb identifies websites. It can recognize technology used to build a website, such as content management systems (CMS), JavaScript libraries, web server and much more. This makes whatweb especially interesting to use in a cascading-rules pipeline. + +To learn more about the whatweb scanner itself visit [https://morningstarsecurity.com/research/whatweb]. +{{- end }} diff --git a/scanners/whatweb/.helmignore b/scanners/whatweb/.helmignore new file mode 100644 index 0000000000..64b2887490 --- /dev/null +++ b/scanners/whatweb/.helmignore @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 +.DS_Store + +parser/ +scanner/ +examples/ +docs/ +integration-tests/ +coverage/ +Makefile diff --git a/scanners/whatweb/Chart.yaml b/scanners/whatweb/Chart.yaml new file mode 100644 index 0000000000..97f1239995 --- /dev/null +++ b/scanners/whatweb/Chart.yaml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: v2 +name: whatweb +description: A Helm chart for the whatweb security Scanner that integrates with the secureCodeBox. + +type: application +# version - gets automatically set to the secureCodeBox release version when the helm charts gets published +version: v3.1.0-alpha1 +appVersion: v0.5.5 +kubeVersion: ">=v1.11.0-0" + +keywords: + - security + - whatweb + - scanner + - secureCodeBox +home: https://docs.securecodebox.io/docs/scanners/Whatweb +icon: https://docs.securecodebox.io/img/integrationIcons/Whatweb.svg +sources: + - https://github.com/secureCodeBox/secureCodeBox +maintainers: + - name: iteratec GmbH + email: secureCodeBox@iteratec.com diff --git a/scanners/whatweb/README.md b/scanners/whatweb/README.md new file mode 100644 index 0000000000..b3663de809 --- /dev/null +++ b/scanners/whatweb/README.md @@ -0,0 +1,50 @@ +--- +title: "Whatweb" +category: "scanner" +type: "Network" +state: "released" +appVersion: "v0.5.5" +usecase: "Website identification" +--- + +![Whatweb logo](https://morningstarsecurity.com/wp-content/uploads/2019/02/WhatWeb-Logo-800px.png) + + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is WHATWEB? +Whatweb identifies websites. It can recognize technology used to build a website, such as content management systems (CMS), JavaScript libraries, web server and much more. This makes whatweb especially interesting to use in a cascading-rules pipeline. + +To learn more about the whatweb scanner itself visit [https://morningstarsecurity.com/research/whatweb]. + +## Deployment +The whatweb chart can be deployed via helm: + +```bash +# Install HelmChart (use -n to configure another namespace) +helm upgrade --install whatweb secureCodeBox/whatweb +``` + diff --git a/scanners/whatweb/cascading-rules/subdomain.yaml b/scanners/whatweb/cascading-rules/subdomain.yaml new file mode 100644 index 0000000000..17fa6b9172 --- /dev/null +++ b/scanners/whatweb/cascading-rules/subdomain.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "cascading.securecodebox.io/v1" +kind: CascadingRule +metadata: + name: "whatweb-subdomain-scan" + labels: + securecodebox.io/invasive: non-invasive + securecodebox.io/intensive: light +spec: + matches: + anyOf: + - category: "Subdomain" + osi_layer: "NETWORK" + scanSpec: + scanType: "whatweb" + parameters: + # Target domain name of the finding and start a whatweb scan + - "{{location}}" diff --git a/scanners/whatweb/docs/.gitkeep b/scanners/whatweb/docs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scanners/whatweb/docs/README.ArtifactHub.md b/scanners/whatweb/docs/README.ArtifactHub.md new file mode 100644 index 0000000000..3ac39d003b --- /dev/null +++ b/scanners/whatweb/docs/README.ArtifactHub.md @@ -0,0 +1,55 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## What is WHATWEB? +Whatweb identifies websites. It can recognize technology used to build a website, such as content management systems (CMS), JavaScript libraries, web server and much more. This makes whatweb especially interesting to use in a cascading-rules pipeline. + +To learn more about the whatweb scanner itself visit [https://morningstarsecurity.com/research/whatweb]. + +## Deployment +The whatweb chart can be deployed via helm: + +```bash +# Install HelmChart (use -n to configure another namespace) +helm upgrade --install whatweb secureCodeBox/whatweb +``` + diff --git a/scanners/whatweb/docs/README.DockerHub-Parser.md b/scanners/whatweb/docs/README.DockerHub-Parser.md new file mode 100644 index 0000000000..64d0399050 --- /dev/null +++ b/scanners/whatweb/docs/README.DockerHub-Parser.md @@ -0,0 +1,82 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `v0.5.5` + +## How to use this image +This `parser` image is intended to work in combination with the corresponding security scanner docker image to parse the `findings` results. For more information details please take a look at the documentation page: https://docs.securecodebox.io/docs/scanners/Whatweb. + +```bash +docker pull securecodebox/parser-whatweb +``` + +## What is WHATWEB? +Whatweb identifies websites. It can recognize technology used to build a website, such as content management systems (CMS), JavaScript libraries, web server and much more. This makes whatweb especially interesting to use in a cascading-rules pipeline. + +To learn more about the whatweb scanner itself visit [https://morningstarsecurity.com/research/whatweb]. + +## Community + +You are welcome, please join us on... 👋 + +- [GitHub][scb-github] +- [Slack][scb-slack] +- [Twitter][scb-twitter] + +secureCodeBox is an official [OWASP][scb-owasp] project. + +## License +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. + +[scb-owasp]: https://www.owasp.org/index.php/OWASP_secureCodeBox +[scb-docs]: https://docs.securecodebox.io/ +[scb-site]: https://www.securecodebox.io/ +[scb-github]: https://github.com/secureCodeBox/ +[scb-twitter]: https://twitter.com/secureCodeBox +[scb-slack]: https://join.slack.com/t/securecodebox/shared_invite/enQtNDU3MTUyOTM0NTMwLTBjOWRjNjVkNGEyMjQ0ZGMyNDdlYTQxYWQ4MzNiNGY3MDMxNThkZjJmMzY2NDRhMTk3ZWM3OWFkYmY1YzUxNTU +[scb-license]: https://github.com/secureCodeBox/secureCodeBox/blob/master/LICENSE diff --git a/scanners/whatweb/docs/README.DockerHub-Scanner.md b/scanners/whatweb/docs/README.DockerHub-Scanner.md new file mode 100644 index 0000000000..1ed7856855 --- /dev/null +++ b/scanners/whatweb/docs/README.DockerHub-Scanner.md @@ -0,0 +1,58 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `v0.5.5` + +## How to use this image +This `scanner` image is intended to work in combination with the corresponding `parser` image to parse the scanner `findings` to generic secureCodeBox results. For more information details please take a look at the [project page][scb-docs] or [documentation page][https://docs.securecodebox.io/docs/scanners/Whatweb]. + +```bash +docker pull securecodebox/scanner-whatweb +``` + +## What is WHATWEB? +Whatweb identifies websites. It can recognize technology used to build a website, such as content management systems (CMS), JavaScript libraries, web server and much more. This makes whatweb especially interesting to use in a cascading-rules pipeline. + +To learn more about the whatweb scanner itself visit [https://morningstarsecurity.com/research/whatweb]. + diff --git a/scanners/whatweb/examples/example.com/scan.yaml b/scanners/whatweb/examples/example.com/scan.yaml new file mode 100644 index 0000000000..6927a35850 --- /dev/null +++ b/scanners/whatweb/examples/example.com/scan.yaml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "whatweb-example" +spec: + scanType: "whatweb" + parameters: + - example.com diff --git a/scanners/whatweb/parser/.dockerignore b/scanners/whatweb/parser/.dockerignore new file mode 100644 index 0000000000..8d754920ee --- /dev/null +++ b/scanners/whatweb/parser/.dockerignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +node_modules/ diff --git a/scanners/whatweb/parser/Dockerfile b/scanners/whatweb/parser/Dockerfile new file mode 100644 index 0000000000..9c9b9fcead --- /dev/null +++ b/scanners/whatweb/parser/Dockerfile @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +ARG namespace +ARG baseImageTag +FROM ${namespace:-securecodebox}/parser-sdk-nodejs:${baseImageTag:-latest} +WORKDIR /home/app/parser-wrapper/parser/ +COPY --chown=app:app ./parser.js ./parser.js diff --git a/scanners/whatweb/parser/__testFiles__/example.com.json b/scanners/whatweb/parser/__testFiles__/example.com.json new file mode 100644 index 0000000000..41ca1a5322 --- /dev/null +++ b/scanners/whatweb/parser/__testFiles__/example.com.json @@ -0,0 +1,3 @@ +[ +{"target":"http://example.com","http_status":200,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"Country":{"string":["EUROPEAN UNION"],"module":["EU"]},"HTML5":{},"HTTPServer":{"string":["ECS (dcb/7FA5)"]},"IP":{"string":["93.184.216.34"]},"Title":{"string":["Example Domain"]}}} +] diff --git a/scanners/whatweb/parser/__testFiles__/no-address.com.json b/scanners/whatweb/parser/__testFiles__/no-address.com.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/scanners/whatweb/parser/__testFiles__/no-address.com.json @@ -0,0 +1,2 @@ +[ +] diff --git a/scanners/whatweb/parser/__testFiles__/securecodebox.io.json b/scanners/whatweb/parser/__testFiles__/securecodebox.io.json new file mode 100644 index 0000000000..02c9e891c2 --- /dev/null +++ b/scanners/whatweb/parser/__testFiles__/securecodebox.io.json @@ -0,0 +1,7 @@ +[ +{"target":"http://securecodebox.io","http_status":301,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"HTTPServer":{"string":["GitHub.com"]},"IP":{"string":["185.199.109.153"]},"RedirectLocation":{"string":["https://www.securecodebox.io/"]},"Title":{"string":["301 Moved Permanently"]},"UncommonHeaders":{"string":["x-github-request-id,x-served-by,x-cache-hits,x-timer,x-fastly-request-id"]},"Via-Proxy":{"string":["1.1 varnish"]}}} +, +{"target":"https://www.securecodebox.io/","http_status":200,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"HTML5":{},"HTTPServer":{"string":["GitHub.com"]},"IP":{"string":["185.199.110.153"]},"Meta-Refresh-Redirect":{"string":["https://docs.securecodebox.io/"]},"Strict-Transport-Security":{"string":["max-age=31556952"]},"Title":{"string":["secureCodeBox – Testing your Software Security"]},"UncommonHeaders":{"string":["access-control-allow-origin,x-proxy-cache,x-github-request-id,x-served-by,x-cache-hits,x-timer,x-fastly-request-id"]},"Via-Proxy":{"string":["1.1 varnish"]}}} +, +{"target":"https://docs.securecodebox.io/","http_status":200,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"Country":{"string":["UNITED STATES"],"module":["US"]},"HTML5":{},"HTTPServer":{"string":["Netlify"]},"IP":{"string":["3.64.200.242"]},"MetaGenerator":{"string":["Docusaurus v2.0.0-beta.3"]},"Open-Graph-Protocol":{},"Script":{},"Strict-Transport-Security":{"string":["max-age=31536000"]},"UncommonHeaders":{"string":["x-nf-request-id"]}}} +] diff --git a/scanners/whatweb/parser/__testFiles__/two-domains.json b/scanners/whatweb/parser/__testFiles__/two-domains.json new file mode 100644 index 0000000000..86569d984b --- /dev/null +++ b/scanners/whatweb/parser/__testFiles__/two-domains.json @@ -0,0 +1,5 @@ +[ +{"target":"http://example.com","http_status":200,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"Country":{"string":["EUROPEAN UNION"],"module":["EU"]},"HTML5":{},"HTTPServer":{"string":["ECS (dcb/7F39)"]},"IP":{"string":["93.184.216.34"]},"Title":{"string":["Example Domain"]}}} +, +{"target":"http://scanme.nmap.org","http_status":200,"request_config":{"headers":{"User-Agent":"WhatWeb/0.5.5"}},"plugins":{"Apache":{"version":["2.4.7"]},"Country":{"string":["RESERVED"],"module":["ZZ"]},"Google-Analytics":{"version":["Universal"],"account":["UA-11009417-1"]},"HTTPServer":{"os":["Ubuntu Linux"],"string":["Apache/2.4.7 (Ubuntu)"]},"IP":{"string":["45.33.32.156"]},"Script":{"string":["text/javascript"]},"Title":{"string":["Go ahead and ScanMe!"]}}} +] diff --git a/scanners/whatweb/parser/parser.js b/scanners/whatweb/parser/parser.js new file mode 100644 index 0000000000..d22f4cb24f --- /dev/null +++ b/scanners/whatweb/parser/parser.js @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +async function parse(fileContent) { + const targets = await parseResultFile(fileContent); + return transformToFindings(targets); +} + +function transformToFindings(targets) { + + const targetFindings = targets.map(target => { + let finding = { + name: target.uri, + category: "WEB APPLICATION", + description: target.title, + location: target.uri, + osi_layer: 'NETWORK', + severity: 'INFORMATIONAL', + attributes: { + requestConfig: target.requestConfig, + ipAddress: target.ipAddress, + country: target.country, + HTML5: target.html5 + } + }; + + target.additional.forEach(additional => { + if (!finding.attributes[additional[0]]) { //Check if key already exists + finding.attributes[additional[0]] = + (("string" in additional[1]) ? additional[1].string[0] : "") + + (("module" in additional[1]) ? "/" + additional[1].module[0] : ""); + } + }); + + if (!finding.attributes.HTML5) //Do not show in findings if undefined + delete finding.attributes.HTML5; + + return finding; + }); + + return [...targetFindings]; +} + +/** + * Parses a given Whatweb JSON file and extracts all targets + * @param {*} fileContent + */ +function parseResultFile(fileContent) { + let targetList = []; + + for(const rawTarget of fileContent) { + if (Object.keys(rawTarget).length > 0) { //Check for empty target + let newTarget = { + uri: rawTarget.target, + httpStatus: rawTarget.http_status, + requestConfig: rawTarget.request_config.headers["User-Agent"], + ipAddress: null, + title: null, + html5: null, + country: null, + additional: [] + } + if(rawTarget.plugins) { + for(const [key, value] of Object.entries(rawTarget.plugins)) { + switch(key) { + case "IP": newTarget.ipAddress = value.string[0]; break; + case "Title": newTarget.title = value.string[0]; break; + case "HTML5": newTarget.html5 = true; break; + case "Country": newTarget.country = value.string[0] + "/" + value.module[0]; break; + default: newTarget.additional.push([key, value]); + } + } + } + targetList.push(newTarget); + } + } + return targetList; +} + +module.exports.parse = parse; diff --git a/scanners/whatweb/parser/parser.test.js b/scanners/whatweb/parser/parser.test.js new file mode 100644 index 0000000000..33fc185eb9 --- /dev/null +++ b/scanners/whatweb/parser/parser.test.js @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +const fs = require("fs"); +const util = require("util"); + +const { + validateParser, +} = require("@securecodebox/parser-sdk-nodejs/parser-utils"); + +// eslint-disable-next-line security/detect-non-literal-fs-filename +const readFile = util.promisify(fs.readFile); + +const { parse } = require("./parser"); + +test("should properly parse whatweb json file", async () => { + const fileContent = JSON.parse( + await readFile(__dirname + "/__testFiles__/example.com.json", { + encoding: "utf8", + }) + ); + const findings = await parse(fileContent); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` +Array [ + Object { + "attributes": Object { + "HTML5": true, + "HTTPServer": "ECS (dcb/7FA5)", + "country": "EUROPEAN UNION/EU", + "ipAddress": "93.184.216.34", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": "Example Domain", + "location": "http://example.com", + "name": "http://example.com", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, +] +`); +}); + +test("should properly parse empty whatweb json file", async () => { + const fileContent = JSON.parse( + await readFile(__dirname + "/__testFiles__/no-address.com.json", { + encoding: "utf8", + }) + ); + const findings = await parse(fileContent); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(`Array []`); +}); + +test("should properly parse securecodebox.io whatweb json file with higher aggression level(3)", async () => { + const fileContent = JSON.parse( + await readFile(__dirname + "/__testFiles__/securecodebox.io.json", { + encoding: "utf8", + }) + ); + const findings = await parse(fileContent); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` +Array [ + Object { + "attributes": Object { + "HTTPServer": "GitHub.com", + "RedirectLocation": "https://www.securecodebox.io/", + "UncommonHeaders": "x-github-request-id,x-served-by,x-cache-hits,x-timer,x-fastly-request-id", + "Via-Proxy": "1.1 varnish", + "country": null, + "ipAddress": "185.199.109.153", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": "301 Moved Permanently", + "location": "http://securecodebox.io", + "name": "http://securecodebox.io", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, + Object { + "attributes": Object { + "HTML5": true, + "HTTPServer": "GitHub.com", + "Meta-Refresh-Redirect": "https://docs.securecodebox.io/", + "Strict-Transport-Security": "max-age=31556952", + "UncommonHeaders": "access-control-allow-origin,x-proxy-cache,x-github-request-id,x-served-by,x-cache-hits,x-timer,x-fastly-request-id", + "Via-Proxy": "1.1 varnish", + "country": null, + "ipAddress": "185.199.110.153", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": "secureCodeBox – Testing your Software Security", + "location": "https://www.securecodebox.io/", + "name": "https://www.securecodebox.io/", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, + Object { + "attributes": Object { + "HTML5": true, + "HTTPServer": "Netlify", + "MetaGenerator": "Docusaurus v2.0.0-beta.3", + "Open-Graph-Protocol": "", + "Script": "", + "Strict-Transport-Security": "max-age=31536000", + "UncommonHeaders": "x-nf-request-id", + "country": "UNITED STATES/US", + "ipAddress": "3.64.200.242", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": null, + "location": "https://docs.securecodebox.io/", + "name": "https://docs.securecodebox.io/", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, +] +`); +}); + +test("should properly parse whatweb json file with two domains", async () => { + const fileContent = JSON.parse( + await readFile(__dirname + "/__testFiles__/two-domains.json", { + encoding: "utf8", + }) + ); + const findings = await parse(fileContent); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` +Array [ + Object { + "attributes": Object { + "HTML5": true, + "HTTPServer": "ECS (dcb/7F39)", + "country": "EUROPEAN UNION/EU", + "ipAddress": "93.184.216.34", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": "Example Domain", + "location": "http://example.com", + "name": "http://example.com", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, + Object { + "attributes": Object { + "Apache": "", + "Google-Analytics": "", + "HTTPServer": "Apache/2.4.7 (Ubuntu)", + "Script": "text/javascript", + "country": "RESERVED/ZZ", + "ipAddress": "45.33.32.156", + "requestConfig": "WhatWeb/0.5.5", + }, + "category": "WEB APPLICATION", + "description": "Go ahead and ScanMe!", + "location": "http://scanme.nmap.org", + "name": "http://scanme.nmap.org", + "osi_layer": "NETWORK", + "severity": "INFORMATIONAL", + }, +] +`); +}); diff --git a/scanners/whatweb/scanner/Dockerfile b/scanners/whatweb/scanner/Dockerfile new file mode 100644 index 0000000000..0e24221cee --- /dev/null +++ b/scanners/whatweb/scanner/Dockerfile @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +FROM ruby:latest + +ARG scannerVersion=v0.5.5 +RUN git clone --depth 1 --branch $scannerVersion https://github.com/urbanadventurer/WhatWeb.git \ + && cd WhatWeb \ + && make install + +RUN addgroup --system --gid 1001 whatweb && adduser whatweb --system --uid 1001 --ingroup whatweb +USER 1001 +CMD ["whatweb"] diff --git a/scanners/whatweb/templates/cascading-rules.yaml b/scanners/whatweb/templates/cascading-rules.yaml new file mode 100644 index 0000000000..97dd683b66 --- /dev/null +++ b/scanners/whatweb/templates/cascading-rules.yaml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +# We only want to import the default cascading rules if they are enabled +{{ if .Values.cascadingRules.enabled }} +# The CascadingRules are not directly in the /templates directory as their curly bracket syntax clashes with helms templates ... :( +# We import them as raw files to avoid these clashes as escaping them is even more messy +{{ range $path, $_ := .Files.Glob "cascading-rules/*" }} +# Include File +{{ $.Files.Get $path }} +# Separate multiple files +--- +{{ end }} +{{ end }} diff --git a/scanners/whatweb/templates/whatweb-parse-definition.yaml b/scanners/whatweb/templates/whatweb-parse-definition.yaml new file mode 100644 index 0000000000..2898ee2d91 --- /dev/null +++ b/scanners/whatweb/templates/whatweb-parse-definition.yaml @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "execution.securecodebox.io/v1" +kind: ParseDefinition +metadata: + name: "whatweb-json" +spec: + image: "{{ .Values.parser.image.repository }}:{{ .Values.parser.image.tag | default .Chart.Version }}" + ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} + env: + {{- toYaml .Values.parser.env | nindent 4 }} diff --git a/scanners/whatweb/templates/whatweb-scan-type.yaml b/scanners/whatweb/templates/whatweb-scan-type.yaml new file mode 100644 index 0000000000..48e5c546dd --- /dev/null +++ b/scanners/whatweb/templates/whatweb-scan-type.yaml @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "execution.securecodebox.io/v1" +kind: ScanType +metadata: + name: "whatweb{{ .Values.scanner.nameAppend | default ""}}" +spec: + extractResults: + type: whatweb-json + location: "/home/securecodebox/whatweb-results.json" + jobTemplate: + spec: + {{- if .Values.scanner.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ .Values.scanner.ttlSecondsAfterFinished }} + {{- end }} + backoffLimit: {{ .Values.scanner.backoffLimit }} + template: + spec: + restartPolicy: Never + containers: + - name: whatweb + image: "{{ .Values.scanner.image.repository }}:{{ .Values.scanner.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.scanner.image.pullPolicy }} + command: ["whatweb", "--log-json=/home/securecodebox/whatweb-results.json"] + resources: + {{- toYaml .Values.scanner.resources | nindent 16 }} + securityContext: + {{- toYaml .Values.scanner.securityContext | nindent 16 }} + env: + {{- toYaml .Values.scanner.env | nindent 16 }} + volumeMounts: + {{- toYaml .Values.scanner.extraVolumeMounts | nindent 16 }} + {{- if .Values.scanner.extraContainers }} + {{- toYaml .Values.scanner.extraContainers | nindent 12 }} + {{- end }} + volumes: + {{- toYaml .Values.scanner.extraVolumes | nindent 12 }} diff --git a/scanners/whatweb/values.yaml b/scanners/whatweb/values.yaml new file mode 100644 index 0000000000..2addc98764 --- /dev/null +++ b/scanners/whatweb/values.yaml @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +parser: + image: + # parser.image.repository -- Parser image repository + repository: docker.io/securecodebox/parser-whatweb + # parser.image.tag -- Parser image tag + # @default -- defaults to the charts appVersion + tag: null + # -- Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + pullPolicy: IfNotPresent + + # parser.ttlSecondsAfterFinished -- seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ + ttlSecondsAfterFinished: null + # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) + env: [] + +scanner: + image: + # scanner.image.repository -- Container Image to run the scan + repository: docker.io/securecodebox/scanner-whatweb + # scanner.image.tag -- defaults to the charts version + tag: null + # -- Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + pullPolicy: IfNotPresent + + # scanner.nameAppend -- append a string to the default scantype name. + nameAppend: null + + # scanner.ttlSecondsAfterFinished -- seconds after which the kubernetes job for the scanner will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ + ttlSecondsAfterFinished: null + # scanner.backoffLimit -- There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) + # @default -- 3 + backoffLimit: 3 + + # scanner.resources -- CPU/memory resource requests/limits (see: https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/, https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/) + resources: {} + # resources: + # requests: + # memory: "256Mi" + # cpu: "250m" + # limits: + # memory: "512Mi" + # cpu: "500m" + + # scanner.env -- Optional environment variables mapped into each scanJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) + env: [] + + # scanner.extraVolumes -- Optional Volumes mapped into each scanJob (see: https://kubernetes.io/docs/concepts/storage/volumes/) + extraVolumes: [] + + # scanner.extraVolumeMounts -- Optional VolumeMounts mapped into each scanJob (see: https://kubernetes.io/docs/concepts/storage/volumes/) + extraVolumeMounts: [] + + # scanner.extraContainers -- Optional additional Containers started with each scanJob (see: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) + extraContainers: [] + + # scanner.securityContext -- Optional securityContext set on scanner container (see: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) + securityContext: + # scanner.securityContext.runAsNonRoot -- Enforces that the scanner image is run as a non root user + runAsNonRoot: false + # scanner.securityContext.readOnlyRootFilesystem -- Prevents write access to the containers file system + readOnlyRootFilesystem: true + # scanner.securityContext.allowPrivilegeEscalation -- Ensure that users privileges cannot be escalated + allowPrivilegeEscalation: false + # scanner.securityContext.privileged -- Ensures that the scanner container is not run in privileged mode + privileged: false + capabilities: + drop: + # scanner.securityContext.capabilities.drop[0] -- This drops all linux privileges from the container. + - all + +cascadingRules: + # cascadingRules.enabled -- Enables or disables the installation of the default cascading rules for this scanner + enabled: true diff --git a/tests/integration/scanner/whatweb.test.js b/tests/integration/scanner/whatweb.test.js new file mode 100644 index 0000000000..80a48ee668 --- /dev/null +++ b/tests/integration/scanner/whatweb.test.js @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +const { scan } = require("../helpers"); + +jest.retryTimes(3); + +test( + "Whatweb scans example.com", + async () => { + const { categories, severities, count } = await scan( + "whatweb-example", + "whatweb", + ["example.com"], + 90 + ); + + expect(count).toBe(1); + expect(categories).toMatchInlineSnapshot(` + Object { + "WEB APPLICATION": 1, + } + `); + expect(severities).toMatchInlineSnapshot(` +Object { + "informational": 1, +} +`); + }, + 3 * 60 * 1000 +); + +test( + "Invalid argument should be marked as errored", + async () => { + await expect( + scan("whatweb-invalidArg", "whatweb", ["--invalidArg", "example.com"], 90) + ).rejects.toThrow("HTTP request failed"); + }, + 3 * 60 * 1000 +);