├── .gitignore ├── LICENSE ├── README.md ├── apps ├── adapter │ ├── README.md │ └── main.go ├── dnsrecon │ └── Dockerfile ├── fn-crtsh │ ├── README.md │ ├── app.js │ ├── ingress.yml │ └── package.json ├── fn-reporting │ ├── README.md │ ├── app.js │ └── package.json ├── fn-sep │ ├── README.md │ ├── app.js │ └── package.json ├── fn-uep │ ├── README.md │ ├── app.js │ └── package.json ├── nmap-workflow │ ├── Dockerfile │ └── README.md ├── report-generator │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── handler.sh │ ├── site-legacy │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── LICENSE │ │ ├── README.md │ │ ├── docs │ │ │ ├── another-page │ │ │ │ └── index.html │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ └── styles.css │ │ │ │ └── images │ │ │ │ │ ├── branching.png │ │ │ │ │ └── octocat.png │ │ │ ├── ejs-page │ │ │ │ └── index.html │ │ │ ├── html-page │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── scripts │ │ │ └── build.js │ │ ├── site.config.js │ │ └── src │ │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── bulma_apsc.css │ │ │ │ ├── font-awesome5-all.js │ │ │ │ └── styles.css │ │ │ └── images │ │ │ │ ├── branching.png │ │ │ │ ├── octocat.png │ │ │ │ └── splat-logo-blue.png │ │ │ ├── layouts │ │ │ ├── default.ejs │ │ │ └── minimal.ejs │ │ │ ├── pages │ │ │ ├── another-page │ │ │ │ └── index.md │ │ │ ├── ejs-page │ │ │ │ └── index.ejs │ │ │ ├── hosts.ejs │ │ │ ├── html-page │ │ │ │ └── index.html │ │ │ ├── index.ejs │ │ │ ├── nmap.ejs │ │ │ └── zap.ejs │ │ │ └── partials │ │ │ ├── foot.ejs │ │ │ └── head.ejs │ └── site.tar.gz ├── report-trigger │ ├── README.md │ ├── app.js │ └── package.json ├── shell-tools │ ├── Dockerfile │ └── README.md └── zap-workflow │ ├── Dockerfile │ └── README.md ├── gitbook ├── README.md ├── SUMMARY.md ├── apps-setup │ ├── configure-minio-service.md │ ├── deploy-http-endpoints-discovery-workflow.md │ ├── deploy-kubeless-functions.md │ ├── deploy-nmap-workflow.md │ ├── deploy-reporting-workflow.md │ └── deploy-zap-workflow.md ├── cluster-setup │ ├── create-cluster-in-gke.md │ ├── install-cert-manager.md │ ├── install-helm-tiller.md │ ├── install-kubeless-with-nats-trigger.md │ ├── install-minio.md │ ├── install-nats.md │ └── install-nginx-ingress.md ├── execution │ ├── submit-reporting-request.md │ └── submit-scan-request.md ├── getting-started.md └── prerequisites.md ├── images └── splat-logo.png ├── infra ├── apps-ingress-tls │ └── nginx-ingress-tls.yaml ├── cert-manager │ └── clusterissuer.yaml ├── helm-rbac │ ├── README.md │ └── helm-rbac.yaml ├── http-endpoint-workflow │ └── http-endpoint-workflow.yaml ├── kubeless │ ├── kubeless-v1.0.2.yaml │ └── nats-v1.0.0-alpha.9.yaml ├── nmap-workflow │ └── nmap-workflow.yaml ├── report-generator │ └── report-generator.yaml └── zap-workflow │ └── zap-workflow.yaml └── presentation ├── Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pdf └── Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pptx /.gitignore: -------------------------------------------------------------------------------- 1 | gitbook/_book 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Appsecco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using Docker with Kubernetes for automating Application Secuity and OSINT workflows 2 | 3 | ![SPLAT](images/splat-logo.png) 4 | 5 | ## Getting Started 6 | 7 | Clone this repository 8 | 9 | ``` 10 | git clone https://github.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows 11 | ``` 12 | 13 | Install `gitbook-cli` for serving documentation locally 14 | 15 | ``` 16 | npm install -g gitbook-cli 17 | ``` 18 | 19 | Start `gitbook` server locally 20 | 21 | ``` 22 | cd using-docker-kubernetes-for-automating-appsec-and-osint-workflows/gitbook 23 | gitbook serve 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /apps/adapter/README.md: -------------------------------------------------------------------------------- 1 | # SPLAT Adapter 2 | 3 | ## Build 4 | 5 | ``` 6 | go get -u github.com/nats-io/go-nats 7 | go get -u github.com/minio/minio-go 8 | ``` 9 | 10 | ``` 11 | go build 12 | ``` 13 | 14 | ## Configuration 15 | 16 | > Enabled through environment variables 17 | 18 | | Environment Variable | Purpose | 19 | | -------------------------- | --------------------------------------------------------------- | 20 | | SPLAT_TOOL_NAME | | 21 | | SPLAT_NATS_URL | | 22 | | SPLAT_NATS_CONSUMER_TOPIC | | 23 | | SPLAT_EXEC_PATTERN | Shell exec with bash -c | 24 | | SPLAT_EXEC_TIMEOUT | Timeout in seconds | 25 | | SPLAT_USE_OUTPUT_FILE_PATH | 26 | | SPLAT_MINIO_ENDPOINT | | 27 | | SPLAT_MINIO_ACCESS_KEY | | 28 | | SPLAT_MINIO_SECRET_KEY | | 29 | | SPLAT_MINIO_FILE_PATTERN | Example: "scans/{{SCAN_ID}}/{{OUTPUT_EVENT}}/data.json | 30 | | SPLAT_MINIO_EVENT_NAME | Name | 31 | | SPLAT_MINIO_CAPTURE_STDOUT | Yes to send stdout to minio. Empty to send output file to Minio | 32 | 33 | 34 | ## Command Placeholders 35 | 36 | These variables can be used to construct the tool command to execute 37 | 38 | `{{TARGET}}` is replaced with input string 39 | `{{OUTPUT_FILE_PATH}}` is replaced with this program's auto generated temporary file. The content of this file will be sent to minio 40 | 41 | Example command line: 42 | 43 | ``` 44 | nmap -sS -sV {{TARGET}} -oX {{OUTPUT_FILE_PATH}} 45 | ``` 46 | -------------------------------------------------------------------------------- /apps/adapter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "runtime" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | minio "github.com/minio/minio-go" 17 | nats "github.com/nats-io/go-nats" 18 | ) 19 | 20 | type PubSubEvent struct { 21 | ScanID string 22 | EventType string 23 | EventValue string 24 | } 25 | 26 | func initLogging() { 27 | // TBD: Should logger be configured? 28 | } 29 | 30 | func getConfigValue(key string) string { 31 | return os.Getenv(key) 32 | } 33 | 34 | func getOutputFilePath() (string, error) { 35 | file, err := ioutil.TempFile("", "execTool") 36 | 37 | if err != nil { 38 | log.Print("Failed to create temporary file: ", err.Error()) 39 | return "", err 40 | } 41 | 42 | file.Close() 43 | fp, err := filepath.Abs(file.Name()) 44 | 45 | if err != nil { 46 | log.Print("Failed to temporary file path: ", err.Error()) 47 | return "", err 48 | } 49 | 50 | return fp, nil 51 | } 52 | 53 | func replaceStrPlaceholders(str string, event *PubSubEvent, outputFilePath string) string { 54 | str = strings.Replace(str, "{{SCAN_ID}}", event.ScanID, -1) 55 | str = strings.Replace(str, "{{OUTPUT_EVENT}}", getConfigValue("SPLAT_MINIO_EVENT_NAME"), -1) 56 | str = strings.Replace(str, "{{TARGET}}", event.EventValue, -1) 57 | str = strings.Replace(str, "{{OUTPUT_FILE_PATH}}", outputFilePath, -1) 58 | str = strings.Replace(str, "{{TIMESTAMP}}", strconv.FormatInt(time.Now().UnixNano(), 10), -1) 59 | 60 | return str 61 | } 62 | 63 | func getExecTimeout() int { 64 | timeout := getConfigValue("SPLAT_EXEC_TIMEOUT") 65 | 66 | if timeout == "" { 67 | timeout = "60" 68 | } 69 | 70 | t, err := strconv.Atoi(timeout) 71 | if err != nil { 72 | return 60 73 | } 74 | 75 | return t 76 | } 77 | 78 | func printUploadStatus(n int64, err error) { 79 | if err != nil { 80 | log.Print("Failed to upload to minio: ", err.Error()) 81 | } else { 82 | log.Printf("Successfully uploaded: size %d", n) 83 | } 84 | } 85 | 86 | func minioDeployOutput(event *PubSubEvent, stdOut bytes.Buffer, outputFilePath string) { 87 | endpoint := getConfigValue("SPLAT_MINIO_ENDPOINT") 88 | accessKeyID := getConfigValue("SPLAT_MINIO_ACCESS_KEY") 89 | secretAccessKey := getConfigValue("SPLAT_MINIO_SECRET_KEY") 90 | useSSL := false 91 | 92 | log.Printf("Deploying STDOUT:%d bytes OutputFile:%s", stdOut.Len(), outputFilePath) 93 | 94 | client, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL) 95 | if err != nil { 96 | log.Print("Failed to connect to Minio endpoint") 97 | return 98 | } 99 | 100 | bucketName := getConfigValue("MINIO_OUTPUT_BUCKET") 101 | location := getConfigValue("SPLAT_MINIO_FILE_PATTERN") 102 | 103 | // location = strings.Replace(location, "{{SCAN_ID}}", event.ScanID, -1) 104 | // location = strings.Replace(location, "{{OUTPUT_EVENT}}", eventName, -1) 105 | location = replaceStrPlaceholders(location, event, getConfigValue("SPLAT_USE_OUTPUT_FILE_PATH")) 106 | 107 | log.Print("Writing to Minio: Bucket: ", bucketName, " Location: ", location) 108 | 109 | contentType := "application/json" 110 | if len(getConfigValue("SPLAT_MINIO_CAPTURE_STDOUT")) > 0 { 111 | log.Print("Sending stdout to Minio") 112 | n, err := client.PutObject(bucketName, location, strings.NewReader(stdOut.String()), -1, minio.PutObjectOptions{ContentType: contentType}) 113 | printUploadStatus(n, err) 114 | } else { 115 | log.Print("Sending output file to Minio") 116 | n, err := client.FPutObject(bucketName, location, outputFilePath, minio.PutObjectOptions{ContentType: contentType}) 117 | printUploadStatus(n, err) 118 | } 119 | } 120 | 121 | func deployOutput(event *PubSubEvent, stdout bytes.Buffer, outputFilePath string) { 122 | minioDeployOutput(event, stdout, outputFilePath) 123 | } 124 | 125 | /* 126 | SPLAT_EXEC_PATTERN="nmap -sT -p 443,80,8080 {{TARGET}} -oX {{OUTPUT_FILE_PATH}}" 127 | */ 128 | 129 | func execToolAndGetOutput(event *PubSubEvent) { 130 | log.Print("Executing external tool on PubSub event") 131 | 132 | var err error 133 | execPattern := getConfigValue("SPLAT_EXEC_PATTERN") 134 | outputFilePath := getConfigValue("SPLAT_USE_OUTPUT_FILE_PATH") 135 | 136 | if len(outputFilePath) == 0 { 137 | outputFilePath, err = getOutputFilePath() 138 | if err != nil { 139 | log.Print("Failed to generated output file path: ", err.Error()) 140 | return 141 | } 142 | } 143 | 144 | // TODO: Shell escape this string 145 | targetStr := event.EventValue 146 | 147 | execPattern = strings.Replace(execPattern, "{{TARGET}}", targetStr, -1) 148 | execPattern = strings.Replace(execPattern, "{{OUTPUT_FILE_PATH}}", outputFilePath, -1) 149 | 150 | log.Print("Running exec pattern: ", execPattern) 151 | 152 | // cmdArray := strings.Split(execPattern, " ") 153 | // cmd := exec.Command(cmdArray[0], cmdArray[1:]...) 154 | 155 | // We need this to be able to pipe shell commands 156 | cmd := exec.Command("sh", "-c", execPattern) 157 | 158 | var stdOut bytes.Buffer 159 | cmd.Stdout = &stdOut 160 | 161 | err = cmd.Start() 162 | 163 | done := make(chan error) 164 | go func() { done <- cmd.Wait() }() 165 | 166 | timeout := time.After(time.Duration(getExecTimeout()) * time.Second) 167 | select { 168 | case <-timeout: 169 | cmd.Process.Kill() 170 | log.Print("Command execution timed out!") 171 | case err := <-done: 172 | if err != nil { 173 | log.Print("Non-zero exit code from command: ", err.Error()) 174 | } else { 175 | log.Print("Command execution finished successfully") 176 | 177 | log.Print("STDOUT: ") 178 | log.Print(stdOut.String()) 179 | deployOutput(event, stdOut, outputFilePath) 180 | } 181 | } 182 | } 183 | 184 | func handleNatsEvent(m *nats.Msg) { 185 | log.Print("Received a message: ", string(m.Data)) 186 | 187 | var event PubSubEvent 188 | err := json.Unmarshal(m.Data, &event) 189 | 190 | if err != nil { 191 | log.Print("Error JSON decoding message: ", err.Error()) 192 | return 193 | } 194 | 195 | execToolAndGetOutput(&event) 196 | } 197 | 198 | func startConsumer() { 199 | log.Print("Starting consumer loop") 200 | nc, err := nats.Connect(getConfigValue("SPLAT_NATS_URL")) 201 | 202 | if err != nil { 203 | log.Fatal("Failed to connect NATS: ", err.Error()) 204 | return 205 | } 206 | 207 | queueGroupName := getConfigValue("SPLAT_QUEUE_GROUP_NAME") 208 | if len(queueGroupName) > 0 { 209 | log.Printf("Using queue subscription with group: %s", queueGroupName) 210 | nc.QueueSubscribe(getConfigValue("SPLAT_NATS_CONSUMER_TOPIC"), queueGroupName, func(m *nats.Msg) { 211 | handleNatsEvent(m) 212 | }) 213 | } else { 214 | log.Print("Using topic subscription") 215 | nc.Subscribe(getConfigValue("SPLAT_NATS_CONSUMER_TOPIC"), func(m *nats.Msg) { 216 | handleNatsEvent(m) 217 | }) 218 | } 219 | 220 | nc.Flush() 221 | runtime.Goexit() // Blocking 222 | } 223 | 224 | func main() { 225 | initLogging() 226 | startConsumer() 227 | } 228 | -------------------------------------------------------------------------------- /apps/dnsrecon/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7-alpine 2 | LABEL MAINTAINER="Appsecco" 3 | 4 | RUN apk update && apk add git \ 5 | && git clone https://github.com/darkoperator/dnsrecon.git \ 6 | && apk add --update --no-cache g++ gcc libxslt-dev 7 | 8 | WORKDIR /dnsrecon 9 | 10 | RUN pip install -r requirements.txt 11 | 12 | ENTRYPOINT [ "python", "/dnsrecon/dnsrecon.py" ] -------------------------------------------------------------------------------- /apps/fn-crtsh/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | Deploy function 4 | 5 | ``` 6 | kubeless function deploy splat-crtsh-scanner -f app.js -d package.json --runtime nodejs8 --handler app.handler 7 | ``` 8 | 9 | Create NATS trigger 10 | 11 | ``` 12 | kubeless trigger nats create crtsh-input-trigger --trigger-topic splat-input-domain --function-selector created-by=kubeless,function=splat-crtsh-scanner 13 | ``` 14 | 15 | ``` 16 | kubeless trigger nats delete crtsh-input-trigger 17 | ``` 18 | -------------------------------------------------------------------------------- /apps/fn-crtsh/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const MODULE_NAME = 'subdomain' 4 | const { Client } = require('pg') 5 | const _ = require('lodash') 6 | 7 | const Minio = require('minio') 8 | 9 | /* 10 | 11 | Environment variables: 12 | 13 | process.env.MINIO_HOST 14 | process.env.MINIO_PORT 15 | process.env.MINIO_ACCESS_KEY 16 | process.env.MINIO_SECRET_KEY 17 | process.env.MINIO_BUCKET_NAME 18 | 19 | */ 20 | 21 | function enumByCrtsh(domain) { 22 | return new Promise(function (resolve, reject) { 23 | const client = new Client({ 24 | connectionString: "postgresql://guest:@crt.sh:5432/certwatch" 25 | }) 26 | 27 | let q = "SELECT ci.NAME_VALUE NAME_VALUE FROM certificate_identity ci WHERE ci.NAME_TYPE = 'dNSName' AND reverse(lower(ci.NAME_VALUE)) LIKE reverse(lower($1))" 28 | 29 | client.connect() 30 | client.query(q, [`%.${domain}`], function (err, res) { 31 | if (err) { 32 | client.end() 33 | reject(err) 34 | } 35 | else { 36 | client.end() 37 | resolve(_.uniqBy(res.rows.map(function (r) { return r.name_value }), function (e) { return e })) 38 | } 39 | }) 40 | }) 41 | } 42 | 43 | function persistToStorage(scanId, domains) { 44 | var minioClient = new Minio.Client({ 45 | endPoint: process.env.MINIO_HOST, 46 | port: parseInt(process.env.MINIO_PORT), 47 | useSSL: false, 48 | accessKey: process.env.MINIO_ACCESS_KEY, 49 | secretKey: process.env.MINIO_SECRET_KEY 50 | }) 51 | 52 | let path = `scans/${scanId}/crtsh/data.json` 53 | let bucket = process.env.MINIO_BUCKET_NAME 54 | 55 | return new Promise(function (resolve, reject) { 56 | minioClient.putObject(bucket, path, JSON.stringify(domains), function (err, etag) { 57 | if (err) { 58 | console.log(`Failed to create Minio object: ${err}`) 59 | return reject(err) 60 | } 61 | else { 62 | console.log(`Created Minio object with ETAG:${etag}`) 63 | return resolve(etag) 64 | } 65 | }) 66 | }) 67 | } 68 | 69 | function enumSubdomains(domain) { 70 | return new Promise(async function (resolve, reject) { 71 | console.log(`Running subdomain enumeration for domain:${domain}`) 72 | 73 | try { 74 | let crtshDomains = await enumByCrtsh(domain) 75 | let moreDomains = [] // TODO: One more enumeration technique 76 | let domains = _.uniqBy(_.concat(crtshDomains, moreDomains), function (e) { return e }) 77 | 78 | resolve({ 79 | module_name: MODULE_NAME, 80 | results: domains 81 | }) 82 | } 83 | catch(error) { 84 | reject(error) 85 | } 86 | }) 87 | } 88 | 89 | async function handler(event, context) { 90 | let request = event.data 91 | 92 | if (typeof(request) === 'string') { 93 | request = JSON.parse(request) 94 | } 95 | 96 | try { 97 | let response = await enumSubdomains(request.target_domain) 98 | await persistToStorage(request.scan_id, response.results) 99 | 100 | return JSON.stringify({ status: "Success" }) 101 | } 102 | catch (err) { 103 | console.log(`Error: ${err}`) 104 | return JSON.stringify({ error: err }) 105 | } 106 | } 107 | 108 | module.exports = { 109 | handler 110 | } 111 | 112 | // let x = async function() { 113 | // let data = await handler({data: {target_domain: 'appsecco.com', scan_id: 'TEST-SCAN-ID' }}, {}) 114 | // console.log(data) 115 | // } 116 | 117 | // x() 118 | 119 | -------------------------------------------------------------------------------- /apps/fn-crtsh/ingress.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: nodejs-crtsh-ingress 5 | annotations: 6 | kubernetes.io/ingress.class: "nginx" 7 | nginx.ingress.kubernetes.io/rewrite-target: / 8 | spec: 9 | rules: 10 | - http: 11 | paths: 12 | - path: /functions/nodejs-crtsh 13 | backend: 14 | serviceName: nodejs-crtsh 15 | servicePort: 8080 16 | 17 | -------------------------------------------------------------------------------- /apps/fn-crtsh/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-crtsh", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lodash": "^4.17.11", 13 | "minio": "^7.0.4", 14 | "pg": "^7.7.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/fn-reporting/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ``` 4 | kubeless function deploy splat-report-gen -f app.js -d package.json --runtime nodejs8 --handler app.handler 5 | ``` 6 | 7 | The function need to be exposed using Ingress. 8 | 9 | 10 | -------------------------------------------------------------------------------- /apps/fn-reporting/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | 5 | Environment variables: 6 | process.env.NATS_URL 7 | 8 | */ 9 | 10 | let NATS = require('nats'); 11 | const NATS_TOPIC = process.env.NATS_TOPIC || 'report-gen-input' 12 | 13 | function publishToNATS(topic, content) { 14 | return new Promise(function (resolve, reject) { 15 | if (typeof(content) === 'object') { 16 | content = JSON.stringify(content) 17 | } 18 | 19 | try { 20 | let nats = NATS.connect(process.env.NATS_URL) 21 | nats.publish(topic, content, function () { 22 | console.log('Submitted to NATS') 23 | nats.close() 24 | resolve() 25 | }) 26 | } catch (err) { 27 | reject(err) 28 | } 29 | }) 30 | } 31 | 32 | 33 | async function handler(event, context) { 34 | let request = event.data 35 | 36 | console.log(`Triggering report for scan_id: ${request.scan_id}`) 37 | 38 | try { 39 | // The message schema matches with what our Go adapter expects 40 | await publishToNATS(NATS_TOPIC, { 41 | ScanID: request.scan_id, 42 | EventType: 'report-gen', 43 | EventValue: request.scan_id 44 | }) 45 | return JSON.stringify({ status: "Success" }) 46 | } 47 | catch (err) { 48 | console.log(`Error occurred: ${err}`) 49 | return JSON.stringify({ status: "Failed" }) 50 | } 51 | } 52 | 53 | module.exports = { 54 | handler 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /apps/fn-reporting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "report-fn-trigger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "nats": "^1.2.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/fn-sep/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ``` 4 | kubeless function deploy splat-sep -f app.js -d package.json --runtime nodejs8 --handler app.handler 5 | 6 | kubeless trigger nats create minio-splat-trigger --trigger-topic minio-bucket-events --function-selector created-by=kubeless,function=splat-sep 7 | kubeless trigger nats delete minio-splat-trigger 8 | ``` 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/fn-sep/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Minio = require('minio') 4 | 5 | /* 6 | 7 | Environment variables: 8 | 9 | process.env.MINIO_HOST 10 | process.env.MINIO_PORT 11 | process.env.MINIO_ACCESS_KEY 12 | process.env.MINIO_SECRET_KEY 13 | process.env.MINIO_BUCKET_NAME 14 | process.env.NATS_URL = "nats://nats_client:SECRET@localhost:4222" 15 | 16 | */ 17 | 18 | const EVENT_INPUT = "input" 19 | const EVENT_HOSTS = "hosts" 20 | const EVENT_URLS = "urls" 21 | 22 | const EVENT_INPUT_NMAP = "nmap-input" 23 | const EVENT_OUTPUT_NMAP = "nmap" 24 | 25 | const EVENT_INPUT_ZAP = "owas-zap-input" 26 | const EVENT_OUTPUT_ZAP = "owasp-zap" 27 | 28 | let minioClient = new Minio.Client({ 29 | endPoint: process.env.MINIO_HOST, 30 | port: parseInt(process.env.MINIO_PORT), 31 | useSSL: false, 32 | accessKey: process.env.MINIO_ACCESS_KEY, 33 | secretKey: process.env.MINIO_SECRET_KEY 34 | }) 35 | 36 | let NATS = require('nats'); 37 | 38 | function getContentFromStorage(fullPath) { 39 | // Path starts with bucket/ 40 | let parts = fullPath.split("/") 41 | let bucket = parts.slice(0,1)[0] 42 | let oName = parts.slice(1).join("/") 43 | 44 | return new Promise(function (resolve, reject) { 45 | minioClient.getObject(bucket, oName, function (err, dataStream) { 46 | let fileContent = '' 47 | 48 | if (err) { 49 | console.log(`Error downloading file from minio: ${err}`) 50 | return reject(err) 51 | } 52 | 53 | dataStream.on('data', function (chunk) { 54 | fileContent += chunk 55 | }) 56 | 57 | dataStream.on('end', function () { 58 | return resolve(fileContent) 59 | }) 60 | 61 | dataStream.on('error', function (err) { 62 | return reject(err) 63 | }) 64 | }) 65 | }) 66 | } 67 | 68 | function publishToNATS(topic, content) { 69 | return new Promise(function (resolve, reject) { 70 | if (typeof(content) === 'object') { 71 | content = JSON.stringify(content) 72 | } 73 | 74 | try { 75 | let nats = NATS.connect(process.env.NATS_URL) 76 | nats.publish(topic, content, function () { 77 | console.log('Submitted to NATS') 78 | nats.close() 79 | resolve() 80 | }) 81 | } catch (err) { 82 | reject(err) 83 | } 84 | }) 85 | } 86 | 87 | function triggerCertshScanner(event) { 88 | let data = JSON.parse(event.eventData) 89 | 90 | if (!data.target_domain) { 91 | throw new Error("Target domain not found in event data") 92 | } 93 | 94 | let params = { 95 | target_domain: data.target_domain, 96 | scan_id: event.scanId 97 | } 98 | 99 | console.log(`Publishing domain input to NATS: ${JSON.stringify(params)}`) 100 | 101 | // Write to NATS run-crtsh topic to invoke scanner (Kubeless) 102 | return new Promise(function (resolve, reject) { 103 | try { 104 | let nats = NATS.connect(process.env.NATS_URL) 105 | nats.publish('splat-input-domain', JSON.stringify(params), function () { 106 | console.log('Submitted to NATS') 107 | nats.close() 108 | resolve() 109 | }) 110 | } catch (err) { 111 | reject(err) 112 | } 113 | }) 114 | } 115 | 116 | function triggerNmapScanner(event) { 117 | // We are getting data from certsh output, which is basically an array of hosts 118 | let data = JSON.parse(event.eventData) 119 | 120 | if (data.length === 0) { 121 | throw new Error("Empty list of targets") 122 | } 123 | 124 | let params = { 125 | ScanID: event.scanId, 126 | EventType: "nmap", 127 | EventValue: data.join(" ") 128 | } 129 | 130 | return publishToNATS('nmap-input', params) 131 | } 132 | 133 | function triggerOwaspZAP(event) { 134 | let content = event.eventData 135 | 136 | return new Promise(function (resolve, reject) { 137 | let promises = [] 138 | 139 | content.split("\n").forEach(function (line) { 140 | let url = line.trim() 141 | 142 | if (url.length > 0) { 143 | let params = { 144 | ScanID: event.scanId, 145 | EventType: "owasp-zap", 146 | EventValue: url 147 | } 148 | 149 | promises.push(publishToNATS("owasp-zap-input", params)) 150 | } 151 | }) 152 | 153 | Promise.all(promises).then(function () { 154 | resolve() 155 | }).catch(function (err) { 156 | reject(err) 157 | }) 158 | }) 159 | } 160 | 161 | function handleEvent(event) { 162 | let [bucket, x, scanId, eventName, fileName] = event.Key.split("/") 163 | let ev = { 164 | minioKey: event.Key, 165 | bucket, 166 | scanId, 167 | eventName, 168 | fileName, 169 | eventData: '' 170 | } 171 | 172 | return new Promise(async function (resolve, reject) { 173 | try { 174 | ev.eventData = await getContentFromStorage(event.Key) 175 | console.log(`Processing event: ${eventName}`) 176 | 177 | if (eventName === "input") { 178 | // Validate input 179 | console.log(`Triggering crtsh scanner`) 180 | await triggerCertshScanner(ev) // NATS write trigger the scanner 181 | 182 | } else if (eventName === "crtsh") { 183 | // do something 184 | console.log(`Triggering nmap and http-endpoints scanner`) 185 | await triggerNmapScanner(ev) 186 | 187 | } else if (eventName === "http-endpoints") { 188 | // do something 189 | // Start ZAP scan job 190 | // for x in list_of_urls 191 | // ./zap_baseline.py -t 192 | // end 193 | console.log(`Triggering ZAP scanner`) 194 | await triggerOwaspZAP(ev) 195 | } 196 | 197 | resolve({}) 198 | 199 | } catch (err) { 200 | console.error(`Error in handling event: ${err}`) 201 | reject(err) 202 | } 203 | }) 204 | } 205 | 206 | async function handler(event, context) { 207 | let request = event.data // This is a Minio format JSON event 208 | 209 | console.log(`Invoked on NATS event: ${request.EventName}`) 210 | if (request.EventName.indexOf("s3:ObjectCreated") === -1) { 211 | // We don't care about read 212 | return JSON.stringify({}) 213 | } 214 | 215 | try { 216 | let response = await handleEvent(request) 217 | return JSON.stringify({ status: "Success" }) 218 | } 219 | catch (err) { 220 | console.log(`Error occurred: ${err}`) 221 | return JSON.stringify({ status: "Failed" }) 222 | } 223 | } 224 | 225 | module.exports = { 226 | handler 227 | } 228 | 229 | 230 | // let x = async function() { 231 | // let data = await handler({data: {Key: 'scandata/scans/db6ed0cf-c1a0-4691-8caf-26b67b78c8f5/input/data.json'} }, {}) 232 | // data = await handler({data: {Key: 'scandata/scans/db6ed0cf-c1a0-4691-8caf-26b67b78c8f5/crtsh/data.json'} }, {}) 233 | // console.log(data) 234 | // } 235 | 236 | // x() 237 | 238 | -------------------------------------------------------------------------------- /apps/fn-sep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sep-fn-handler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "minio": "^7.0.4", 13 | "nats": "^1.2.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/fn-uep/README.md: -------------------------------------------------------------------------------- 1 | # Kubeless Function (UEP) 2 | 3 | > `MINIO_BUCKET` should be pre-created in Minio Server 4 | 5 | Environment variables for configuration: 6 | 7 | ```bash 8 | export MINIO_HOST='minio.default.svc.cluster.local' 9 | export MINIO_PORT='9000' 10 | export MINIO_ACCESS_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.accesskey}' | base64 -d) 11 | export MINIO_SECRET_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.secretkey}' | base64 -d) 12 | export MINIO_BUCKET_NAME='scandata' 13 | ``` 14 | 15 | Deployment 16 | 17 | ``` 18 | kubeless function deploy splat-uep -f app.js -d package.json --runtime nodejs8 --handler app.handler --env MINIO_HOST=$MINIO_HOST,MINIO_PORT=$MINIO_PORT,MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY,MINIO_SECRET_KEY=$MINIO_SECRET_KEY,MINIO_BUCKET_NAME=$MINIO_BUCKET_NAME 19 | ``` 20 | 21 | Ensure Minio is configured to send bucket write events to NATS 22 | 23 | ``` 24 | mc event add minio/scandata arn:minio:sqs:us-east-1:1:nats 25 | ``` 26 | -------------------------------------------------------------------------------- /apps/fn-uep/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const uuid4 = require('uuid4') 4 | const Minio = require('minio') 5 | 6 | /* 7 | 8 | Environment variables: 9 | 10 | process.env.MINIO_HOST 11 | process.env.MINIO_PORT 12 | process.env.MINIO_ACCESS_KEY 13 | process.env.MINIO_SECRET_KEY 14 | process.env.MINIO_BUCKET_NAME 15 | 16 | */ 17 | 18 | function persist(bucket, path, content) { 19 | var minioClient = new Minio.Client({ 20 | endPoint: process.env.MINIO_HOST, 21 | port: parseInt(process.env.MINIO_PORT), 22 | useSSL: false, 23 | accessKey: process.env.MINIO_ACCESS_KEY, 24 | secretKey: process.env.MINIO_SECRET_KEY 25 | }) 26 | 27 | return new Promise(function (resolve, reject) { 28 | minioClient.putObject(bucket, path, JSON.stringify(content), function (err, etag) { 29 | if (err) { 30 | console.log(`Failed to create Minio object: ${err}`) 31 | return reject(err) 32 | } 33 | else { 34 | console.log(`Created Minio object with ETAG:${etag}`) 35 | return resolve(etag) 36 | } 37 | }) 38 | }) 39 | } 40 | 41 | function initScan(request) { 42 | let params = { 43 | scan_id: uuid4(), 44 | target_domain: request.target_domain 45 | } 46 | 47 | return new Promise(function (resolve, reject) { 48 | persist(process.env.MINIO_BUCKET_NAME, `scans/${params.scan_id}/input/data.json`, params) 49 | .then(function (res) { 50 | resolve(params) 51 | }) 52 | .catch(function (err) { 53 | reject(err) 54 | }) 55 | }) 56 | } 57 | 58 | async function handler(event, context) { 59 | let request = event.data 60 | 61 | try { 62 | let response = await initScan(request) 63 | return JSON.stringify(response) 64 | } 65 | catch (err) { 66 | console.log(`Error occurred: ${err}`) 67 | return JSON.stringify({ error: "Error occurred" }) 68 | } 69 | } 70 | 71 | module.exports = { 72 | handler 73 | } 74 | 75 | 76 | // let x = async function() { 77 | // let data = await handler({data: {target_domain: 'appsecco.com'}}, {}) 78 | // console.log(data) 79 | // } 80 | 81 | // x() 82 | 83 | -------------------------------------------------------------------------------- /apps/fn-uep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uep-fn-handler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "minio": "^7.0.4", 13 | "uuid4": "^1.1.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/nmap-workflow/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM uzyexe/nmap 2 | LABEL MAINTAINER "Appsecco" 3 | 4 | COPY splat-sidecar /usr/bin/splat-sidecar 5 | -------------------------------------------------------------------------------- /apps/nmap-workflow/README.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | > Copy adapter binary to this folder as `splat-sidecar` before building docker image 4 | 5 | Build docker image 6 | 7 | ``` 8 | docker build -t REGISTRY/IMAGE . 9 | ``` 10 | 11 | Push docker image 12 | 13 | ``` 14 | gcloud docker -- push REGISTRY/IMAGE 15 | ``` 16 | -------------------------------------------------------------------------------- /apps/report-generator/.dockerignore: -------------------------------------------------------------------------------- 1 | site-legacy 2 | -------------------------------------------------------------------------------- /apps/report-generator/.gitignore: -------------------------------------------------------------------------------- 1 | google-sa.json 2 | -------------------------------------------------------------------------------- /apps/report-generator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.15.1-stretch-slim 2 | LABEL MAINTAINER "Appsecco" 3 | 4 | ENV PATH $PATH:/root/google-cloud-sdk/bin 5 | RUN apt-get update && apt-get install -y python curl bash build-essential wget \ 6 | && wget https://dl.minio.io/client/mc/release/linux-amd64/mc && chmod +x mc \ 7 | && mv mc /usr/bin/mc 8 | 9 | RUN curl -sSL https://sdk.cloud.google.com | bash 10 | 11 | WORKDIR /app 12 | 13 | COPY site.tar.gz . 14 | COPY splat-sidecar . 15 | COPY handler.sh . 16 | 17 | RUN tar xzvf site.tar.gz && cd site-legacy && cd /app/site-legacy/ && npm install \ 18 | && cd .. && chmod 755 splat-sidecar handler.sh 19 | -------------------------------------------------------------------------------- /apps/report-generator/README.md: -------------------------------------------------------------------------------- 1 | rm site.tar.gz && tar czvf site.tar.gz site-legacy 2 | 3 | docker build -t eu.gcr.io/verdant-wares-173515/splat-reporting . 4 | gcloud docker -- push eu.gcr.io/verdant-wares-173515/splat-reporting 5 | 6 | For cloud storage testing, created bucket `splat-reports` in black widow cluster. 7 | 8 | kubectl create secret generic gcloud --from-file=storage-sa=./google-sa.json --dry-run -o yaml 9 | 10 | rm site.tar.gz && tar czvf site.tar.gz site-legacy 11 | -------------------------------------------------------------------------------- /apps/report-generator/handler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | MINIO_CLIENT="/usr/bin/mc" 4 | NANOGEN="site-legacy" 5 | SCAN_ID="$1" 6 | OUTPUT_FILE_PATH="$2" 7 | CURRDIR=`pwd` 8 | WORKDIR=`mktemp -d` 9 | MASTER_SITE_DIR="/data/master_site" 10 | 11 | #GOOGLE_STORAGE_BUCKET_BASE_URI="gs://splat-reports/reports" 12 | # Should be set as config environment 13 | #GOOGLE_CLOUD_SERVICE_ACCOUNT 14 | # Should be set as environment mapped from Kubernetes secret 15 | 16 | echo "Running builder with scan_id:$SCAN_ID output:$OUTPUT_FILE_PATH" 17 | echo "Using workdir: $WORKDIR" 18 | 19 | # Init minio client - Env names are same as sidecar 20 | $MINIO_CLIENT config host add minio http://$SPLAT_MINIO_ENDPOINT $SPLAT_MINIO_ACCESS_KEY $SPLAT_MINIO_SECRET_KEY 21 | 22 | # Copy site template to workdir 23 | cp -r $NANOGEN $WORKDIR/$NANOGEN 24 | 25 | # Copy scan data 26 | $MINIO_CLIENT cp --recursive minio/scandata/scans/$SCAN_ID/ $WORKDIR/$NANOGEN/src/data/ 27 | 28 | # Execute build script 29 | cd $WORKDIR/$NANOGEN && npm run build && tar czvf $OUTPUT_FILE_PATH public 30 | 31 | # Update the master site 32 | #mkdir -p $MASTER_SITE_DIR 33 | #mkdir -p $MASTER_SITE_DIR/reports/$SCAN_ID 34 | #rsync -avz $WORKDIR/$NANOGEN/public/ $MASTER_SITE_DIR/reports/$SCAN_ID/ 35 | 36 | echo "Starting upload to google cloud storage" 37 | 38 | # Send archive to Google Cloud Storage 39 | #echo $GOOGLE_CLOUD_SERVICE_ACCOUNT | base64 -d > /tmp/sa.json 40 | #echo $GOOGLE_CLOUD_SERVICE_ACCOUNT > /tmp/sa.json 41 | 42 | #RAND_ID=`shuf -i 1-100000 -n 1` 43 | RAND_ID=`date +%b-%d-%Y-%I%M%S` 44 | 45 | #printenv GOOGLE_CLOUD_SERVICE_ACCOUNT > /tmp/sa.json 46 | gcloud auth activate-service-account --key-file /opt/secret/sa.json 2>&1 47 | 48 | gsutil cp $OUTPUT_FILE_PATH $GOOGLE_STORAGE_BUCKET_BASE_URI/$SCAN_ID/report-$RAND_ID.tar.gz 2>&1 49 | exit 0 50 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | .DS_Store 4 | node_modules 5 | public 6 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/.prettierrc: -------------------------------------------------------------------------------- 1 | singleQuote: true -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Douglas Matoso 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/README.md: -------------------------------------------------------------------------------- 1 | # NanoGen 2 | 3 | Micro static site generator in Node.js 4 | 5 | See the post: https://medium.com/douglas-matoso-english/build-static-site-generator-nodejs-8969ebe34b22 6 | 7 | See the example site generated at: https://doug2k1.github.io/nanogen 8 | 9 | ## Setup 10 | 11 | ```console 12 | $ npm i 13 | $ npm run build 14 | $ npm run serve 15 | ``` 16 | 17 | Go to http://localhost:5000 to see the generated site. 18 | 19 | ## How to use 20 | 21 | If you want to use NanoGen to generate your own site, just fork this repository and add your content to the `src` folder. 22 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/another-page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Another Page | NanoGen 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 |
19 |

Welcome to another page

20 |

Markdown page using a different layout.

21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | html { 3 | font-family: sans-serif; 4 | -ms-text-size-adjust: 100%; 5 | -webkit-text-size-adjust: 100% 6 | } 7 | 8 | body { 9 | margin: 0 10 | } 11 | 12 | article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { 13 | display: block 14 | } 15 | 16 | audio, canvas, progress, video { 17 | display: inline-block; 18 | vertical-align: baseline 19 | } 20 | 21 | audio:not([controls]) { 22 | display: none; 23 | height: 0 24 | } 25 | 26 | [hidden], template { 27 | display: none 28 | } 29 | 30 | a { 31 | background-color: transparent 32 | } 33 | 34 | a:active, a:hover { 35 | outline: 0 36 | } 37 | 38 | abbr[title] { 39 | border-bottom: 1px dotted 40 | } 41 | 42 | b, strong { 43 | font-weight: bold 44 | } 45 | 46 | dfn { 47 | font-style: italic 48 | } 49 | 50 | h1 { 51 | font-size: 2em; 52 | margin: 0.67em 0 53 | } 54 | 55 | mark { 56 | background: #ff0; 57 | color: #000 58 | } 59 | 60 | small { 61 | font-size: 80% 62 | } 63 | 64 | sub, sup { 65 | font-size: 75%; 66 | line-height: 0; 67 | position: relative; 68 | vertical-align: baseline 69 | } 70 | 71 | sup { 72 | top: -0.5em 73 | } 74 | 75 | sub { 76 | bottom: -0.25em 77 | } 78 | 79 | img { 80 | border: 0 81 | } 82 | 83 | svg:not(:root) { 84 | overflow: hidden 85 | } 86 | 87 | figure { 88 | margin: 1em 40px 89 | } 90 | 91 | hr { 92 | box-sizing: content-box; 93 | height: 0 94 | } 95 | 96 | pre { 97 | overflow: auto 98 | } 99 | 100 | code, kbd, pre, samp { 101 | font-family: monospace, monospace; 102 | font-size: 1em 103 | } 104 | 105 | button, input, optgroup, select, textarea { 106 | color: inherit; 107 | font: inherit; 108 | margin: 0 109 | } 110 | 111 | button { 112 | overflow: visible 113 | } 114 | 115 | button, select { 116 | text-transform: none 117 | } 118 | 119 | button, html input[type="button"], input[type="reset"], input[type="submit"] { 120 | -webkit-appearance: button; 121 | cursor: pointer 122 | } 123 | 124 | button[disabled], html input[disabled] { 125 | cursor: default 126 | } 127 | 128 | button::-moz-focus-inner, input::-moz-focus-inner { 129 | border: 0; 130 | padding: 0 131 | } 132 | 133 | input { 134 | line-height: normal 135 | } 136 | 137 | input[type="checkbox"], input[type="radio"] { 138 | box-sizing: border-box; 139 | padding: 0 140 | } 141 | 142 | input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { 143 | height: auto 144 | } 145 | 146 | input[type="search"] { 147 | -webkit-appearance: textfield; 148 | box-sizing: content-box 149 | } 150 | 151 | input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { 152 | -webkit-appearance: none 153 | } 154 | 155 | fieldset { 156 | border: 1px solid #c0c0c0; 157 | margin: 0 2px; 158 | padding: 0.35em 0.625em 0.75em 159 | } 160 | 161 | legend { 162 | border: 0; 163 | padding: 0 164 | } 165 | 166 | textarea { 167 | overflow: auto 168 | } 169 | 170 | optgroup { 171 | font-weight: bold 172 | } 173 | 174 | table { 175 | border-collapse: collapse; 176 | border-spacing: 0 177 | } 178 | 179 | td, th { 180 | padding: 0 181 | } 182 | 183 | .highlight table td { 184 | padding: 5px 185 | } 186 | 187 | .highlight table pre { 188 | margin: 0 189 | } 190 | 191 | .highlight .cm { 192 | color: #999988; 193 | font-style: italic 194 | } 195 | 196 | .highlight .cp { 197 | color: #999999; 198 | font-weight: bold 199 | } 200 | 201 | .highlight .c1 { 202 | color: #999988; 203 | font-style: italic 204 | } 205 | 206 | .highlight .cs { 207 | color: #999999; 208 | font-weight: bold; 209 | font-style: italic 210 | } 211 | 212 | .highlight .c, .highlight .cd { 213 | color: #999988; 214 | font-style: italic 215 | } 216 | 217 | .highlight .err { 218 | color: #a61717; 219 | background-color: #e3d2d2 220 | } 221 | 222 | .highlight .gd { 223 | color: #000000; 224 | background-color: #ffdddd 225 | } 226 | 227 | .highlight .ge { 228 | color: #000000; 229 | font-style: italic 230 | } 231 | 232 | .highlight .gr { 233 | color: #aa0000 234 | } 235 | 236 | .highlight .gh { 237 | color: #999999 238 | } 239 | 240 | .highlight .gi { 241 | color: #000000; 242 | background-color: #ddffdd 243 | } 244 | 245 | .highlight .go { 246 | color: #888888 247 | } 248 | 249 | .highlight .gp { 250 | color: #555555 251 | } 252 | 253 | .highlight .gs { 254 | font-weight: bold 255 | } 256 | 257 | .highlight .gu { 258 | color: #aaaaaa 259 | } 260 | 261 | .highlight .gt { 262 | color: #aa0000 263 | } 264 | 265 | .highlight .kc { 266 | color: #000000; 267 | font-weight: bold 268 | } 269 | 270 | .highlight .kd { 271 | color: #000000; 272 | font-weight: bold 273 | } 274 | 275 | .highlight .kn { 276 | color: #000000; 277 | font-weight: bold 278 | } 279 | 280 | .highlight .kp { 281 | color: #000000; 282 | font-weight: bold 283 | } 284 | 285 | .highlight .kr { 286 | color: #000000; 287 | font-weight: bold 288 | } 289 | 290 | .highlight .kt { 291 | color: #445588; 292 | font-weight: bold 293 | } 294 | 295 | .highlight .k, .highlight .kv { 296 | color: #000000; 297 | font-weight: bold 298 | } 299 | 300 | .highlight .mf { 301 | color: #009999 302 | } 303 | 304 | .highlight .mh { 305 | color: #009999 306 | } 307 | 308 | .highlight .il { 309 | color: #009999 310 | } 311 | 312 | .highlight .mi { 313 | color: #009999 314 | } 315 | 316 | .highlight .mo { 317 | color: #009999 318 | } 319 | 320 | .highlight .m, .highlight .mb, .highlight .mx { 321 | color: #009999 322 | } 323 | 324 | .highlight .sb { 325 | color: #d14 326 | } 327 | 328 | .highlight .sc { 329 | color: #d14 330 | } 331 | 332 | .highlight .sd { 333 | color: #d14 334 | } 335 | 336 | .highlight .s2 { 337 | color: #d14 338 | } 339 | 340 | .highlight .se { 341 | color: #d14 342 | } 343 | 344 | .highlight .sh { 345 | color: #d14 346 | } 347 | 348 | .highlight .si { 349 | color: #d14 350 | } 351 | 352 | .highlight .sx { 353 | color: #d14 354 | } 355 | 356 | .highlight .sr { 357 | color: #009926 358 | } 359 | 360 | .highlight .s1 { 361 | color: #d14 362 | } 363 | 364 | .highlight .ss { 365 | color: #990073 366 | } 367 | 368 | .highlight .s { 369 | color: #d14 370 | } 371 | 372 | .highlight .na { 373 | color: #008080 374 | } 375 | 376 | .highlight .bp { 377 | color: #999999 378 | } 379 | 380 | .highlight .nb { 381 | color: #0086B3 382 | } 383 | 384 | .highlight .nc { 385 | color: #445588; 386 | font-weight: bold 387 | } 388 | 389 | .highlight .no { 390 | color: #008080 391 | } 392 | 393 | .highlight .nd { 394 | color: #3c5d5d; 395 | font-weight: bold 396 | } 397 | 398 | .highlight .ni { 399 | color: #800080 400 | } 401 | 402 | .highlight .ne { 403 | color: #990000; 404 | font-weight: bold 405 | } 406 | 407 | .highlight .nf { 408 | color: #990000; 409 | font-weight: bold 410 | } 411 | 412 | .highlight .nl { 413 | color: #990000; 414 | font-weight: bold 415 | } 416 | 417 | .highlight .nn { 418 | color: #555555 419 | } 420 | 421 | .highlight .nt { 422 | color: #000080 423 | } 424 | 425 | .highlight .vc { 426 | color: #008080 427 | } 428 | 429 | .highlight .vg { 430 | color: #008080 431 | } 432 | 433 | .highlight .vi { 434 | color: #008080 435 | } 436 | 437 | .highlight .nv { 438 | color: #008080 439 | } 440 | 441 | .highlight .ow { 442 | color: #000000; 443 | font-weight: bold 444 | } 445 | 446 | .highlight .o { 447 | color: #000000; 448 | font-weight: bold 449 | } 450 | 451 | .highlight .w { 452 | color: #bbbbbb 453 | } 454 | 455 | .highlight { 456 | background-color: #f8f8f8 457 | } 458 | 459 | * { 460 | box-sizing: border-box 461 | } 462 | 463 | body { 464 | padding: 0; 465 | margin: 0; 466 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 467 | font-size: 16px; 468 | line-height: 1.5; 469 | color: #606c71 470 | } 471 | 472 | a { 473 | color: #1e6bb8; 474 | text-decoration: none 475 | } 476 | 477 | a:hover { 478 | text-decoration: underline 479 | } 480 | 481 | .btn { 482 | display: inline-block; 483 | margin-bottom: 1rem; 484 | color: rgba(255, 255, 255, 0.7); 485 | background-color: rgba(255, 255, 255, 0.08); 486 | border-color: rgba(255, 255, 255, 0.2); 487 | border-style: solid; 488 | border-width: 1px; 489 | border-radius: 0.3rem; 490 | transition: color 0.2s, background-color 0.2s, border-color 0.2s 491 | } 492 | 493 | .btn:hover { 494 | color: rgba(255, 255, 255, 0.8); 495 | text-decoration: none; 496 | background-color: rgba(255, 255, 255, 0.2); 497 | border-color: rgba(255, 255, 255, 0.3) 498 | } 499 | 500 | .btn + .btn { 501 | margin-left: 1rem 502 | } 503 | 504 | @media screen and (min-width: 64em) { 505 | .btn { 506 | padding: 0.75rem 1rem 507 | } 508 | } 509 | 510 | @media screen and (min-width: 42em) and (max-width: 64em) { 511 | .btn { 512 | padding: 0.6rem 0.9rem; 513 | font-size: 0.9rem 514 | } 515 | } 516 | 517 | @media screen and (max-width: 42em) { 518 | .btn { 519 | display: block; 520 | width: 100%; 521 | padding: 0.75rem; 522 | font-size: 0.9rem 523 | } 524 | 525 | .btn + .btn { 526 | margin-top: 1rem; 527 | margin-left: 0 528 | } 529 | } 530 | 531 | .page-header { 532 | color: #fff; 533 | text-align: center; 534 | background-color: #159957; 535 | background-image: linear-gradient(120deg, #155799, #159957) 536 | } 537 | 538 | @media screen and (min-width: 64em) { 539 | .page-header { 540 | padding: 5rem 6rem 541 | } 542 | } 543 | 544 | @media screen and (min-width: 42em) and (max-width: 64em) { 545 | .page-header { 546 | padding: 3rem 4rem 547 | } 548 | } 549 | 550 | @media screen and (max-width: 42em) { 551 | .page-header { 552 | padding: 2rem 1rem 553 | } 554 | } 555 | 556 | .project-name { 557 | margin-top: 0; 558 | margin-bottom: 0.1rem 559 | } 560 | 561 | @media screen and (min-width: 64em) { 562 | .project-name { 563 | font-size: 3.25rem 564 | } 565 | } 566 | 567 | @media screen and (min-width: 42em) and (max-width: 64em) { 568 | .project-name { 569 | font-size: 2.25rem 570 | } 571 | } 572 | 573 | @media screen and (max-width: 42em) { 574 | .project-name { 575 | font-size: 1.75rem 576 | } 577 | } 578 | 579 | .project-tagline { 580 | margin-bottom: 2rem; 581 | font-weight: normal; 582 | opacity: 0.7 583 | } 584 | 585 | @media screen and (min-width: 64em) { 586 | .project-tagline { 587 | font-size: 1.25rem 588 | } 589 | } 590 | 591 | @media screen and (min-width: 42em) and (max-width: 64em) { 592 | .project-tagline { 593 | font-size: 1.15rem 594 | } 595 | } 596 | 597 | @media screen and (max-width: 42em) { 598 | .project-tagline { 599 | font-size: 1rem 600 | } 601 | } 602 | 603 | .main-content { 604 | word-wrap: break-word 605 | } 606 | 607 | .main-content :first-child { 608 | margin-top: 0 609 | } 610 | 611 | @media screen and (min-width: 64em) { 612 | .main-content { 613 | max-width: 64rem; 614 | padding: 2rem 6rem; 615 | margin: 0 auto; 616 | font-size: 1.1rem 617 | } 618 | } 619 | 620 | @media screen and (min-width: 42em) and (max-width: 64em) { 621 | .main-content { 622 | padding: 2rem 4rem; 623 | font-size: 1.1rem 624 | } 625 | } 626 | 627 | @media screen and (max-width: 42em) { 628 | .main-content { 629 | padding: 2rem 1rem; 630 | font-size: 1rem 631 | } 632 | } 633 | 634 | .main-content img { 635 | max-width: 100% 636 | } 637 | 638 | .main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 { 639 | margin-top: 2rem; 640 | margin-bottom: 1rem; 641 | font-weight: normal; 642 | color: #159957 643 | } 644 | 645 | .main-content p { 646 | margin-bottom: 1em 647 | } 648 | 649 | .main-content code { 650 | padding: 2px 4px; 651 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 652 | font-size: 0.9rem; 653 | color: #567482; 654 | background-color: #f3f6fa; 655 | border-radius: 0.3rem 656 | } 657 | 658 | .main-content pre { 659 | padding: 0.8rem; 660 | margin-top: 0; 661 | margin-bottom: 1rem; 662 | font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; 663 | color: #567482; 664 | word-wrap: normal; 665 | background-color: #f3f6fa; 666 | border: solid 1px #dce6f0; 667 | border-radius: 0.3rem 668 | } 669 | 670 | .main-content pre > code { 671 | padding: 0; 672 | margin: 0; 673 | font-size: 0.9rem; 674 | color: #567482; 675 | word-break: normal; 676 | white-space: pre; 677 | background: transparent; 678 | border: 0 679 | } 680 | 681 | .main-content .highlight { 682 | margin-bottom: 1rem 683 | } 684 | 685 | .main-content .highlight pre { 686 | margin-bottom: 0; 687 | word-break: normal 688 | } 689 | 690 | .main-content .highlight pre, .main-content pre { 691 | padding: 0.8rem; 692 | overflow: auto; 693 | font-size: 0.9rem; 694 | line-height: 1.45; 695 | border-radius: 0.3rem; 696 | -webkit-overflow-scrolling: touch 697 | } 698 | 699 | .main-content pre code, .main-content pre tt { 700 | display: inline; 701 | max-width: initial; 702 | padding: 0; 703 | margin: 0; 704 | overflow: initial; 705 | line-height: inherit; 706 | word-wrap: normal; 707 | background-color: transparent; 708 | border: 0 709 | } 710 | 711 | .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { 712 | content: normal 713 | } 714 | 715 | .main-content ul, .main-content ol { 716 | margin-top: 0 717 | } 718 | 719 | .main-content blockquote { 720 | padding: 0 1rem; 721 | margin-left: 0; 722 | color: #819198; 723 | border-left: 0.3rem solid #dce6f0 724 | } 725 | 726 | .main-content blockquote > :first-child { 727 | margin-top: 0 728 | } 729 | 730 | .main-content blockquote > :last-child { 731 | margin-bottom: 0 732 | } 733 | 734 | .main-content table { 735 | display: block; 736 | width: 100%; 737 | overflow: auto; 738 | word-break: normal; 739 | word-break: keep-all; 740 | -webkit-overflow-scrolling: touch 741 | } 742 | 743 | .main-content table th { 744 | font-weight: bold 745 | } 746 | 747 | .main-content table th, .main-content table td { 748 | padding: 0.5rem 1rem; 749 | border: 1px solid #e9ebec 750 | } 751 | 752 | .main-content dl { 753 | padding: 0 754 | } 755 | 756 | .main-content dl dt { 757 | padding: 0; 758 | margin-top: 1rem; 759 | font-size: 1rem; 760 | font-weight: bold 761 | } 762 | 763 | .main-content dl dd { 764 | padding: 0; 765 | margin-bottom: 1rem 766 | } 767 | 768 | .main-content hr { 769 | height: 2px; 770 | padding: 0; 771 | margin: 1rem 0; 772 | background-color: #eff0f1; 773 | border: 0 774 | } 775 | 776 | .site-footer { 777 | padding-top: 2rem; 778 | margin-top: 2rem; 779 | border-top: solid 1px #eff0f1 780 | } 781 | 782 | @media screen and (min-width: 64em) { 783 | .site-footer { 784 | font-size: 1rem 785 | } 786 | } 787 | 788 | @media screen and (min-width: 42em) and (max-width: 64em) { 789 | .site-footer { 790 | font-size: 1rem 791 | } 792 | } 793 | 794 | @media screen and (max-width: 42em) { 795 | .site-footer { 796 | font-size: 0.9rem 797 | } 798 | } 799 | 800 | .site-footer-owner { 801 | display: block; 802 | font-weight: bold 803 | } 804 | 805 | .site-footer-credits { 806 | color: #819198 807 | } 808 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/assets/images/branching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site-legacy/docs/assets/images/branching.png -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/assets/images/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site-legacy/docs/assets/images/octocat.png -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/ejs-page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EJS Page | NanoGen 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 |
25 |

EJS Page!

26 | 27 |

Here are some variables: NanoGen | EJS Page

28 | 29 |

Projects

30 | 31 |

The data below is coming from a JSON file.

32 | 33 | 40 | 41 | 42 |
43 | This page was generated by NanoGen. 44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/html-page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML Page | NanoGen 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 |
25 |

HTML page!

26 | 27 |

This is just HTML, inserted as is into the layout.

28 | 29 | 30 |
31 | This page was generated by NanoGen. 32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NanoGen 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 |
25 |

This example site was generated with NanoGen, a micro static site generator built for learning purposes.

26 |

Text can be bold, italic, or strikethrough.

27 |

Link to another page.

28 |

There should be whitespace between paragraphs.

29 |

There should be whitespace between paragraphs. We recommend including a README, or a file with information about your project.

30 |

Header 1

31 |

This is a normal paragraph following a header. GitHub is a code hosting platform for version control and collaboration. It lets you and others work together on projects from anywhere.

32 |

Header 2

33 |
34 |

This is a blockquote following a header.

35 |

When something is important enough, you do it even if the odds are not in your favor.

36 |
37 |

Header 3

38 |
// Javascript code with syntax highlighting.
 39 | var fun = function lang(l) {
 40 |   dateformat.i18n = require('./lang/' + l)
 41 |   return true;
 42 | }
 43 | 
44 |
# Ruby code with syntax highlighting
 45 | GitHubPages::Dependencies.gems.each do |gem, version|
 46 |   s.add_dependency(gem, "= #{version}")
 47 | end
 48 | 
49 |

Header 4

50 |
    51 |
  • This is an unordered list following a header.
  • 52 |
  • This is an unordered list following a header.
  • 53 |
  • This is an unordered list following a header.
  • 54 |
55 |
Header 5
56 |
    57 |
  1. This is an ordered list following a header.
  2. 58 |
  3. This is an ordered list following a header.
  4. 59 |
  5. This is an ordered list following a header.
  6. 60 |
61 |
Header 6
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
head1head twothree
okgood swedish fishnice
out of stockgood and plentynice
okgood oreoshmm
okgood zoute dropyumm
93 |

There's a horizontal rule below this.

94 |
95 |

Here is an unordered list:

96 |
    97 |
  • Item foo
  • 98 |
  • Item bar
  • 99 |
  • Item baz
  • 100 |
  • Item zip
  • 101 |
102 |

And an ordered list:

103 |
    104 |
  1. Item one
  2. 105 |
  3. Item two
  4. 106 |
  5. Item three
  6. 107 |
  7. Item four
  8. 108 |
109 |

And a nested list:

110 |
    111 |
  • level 1 item
      112 |
    • level 2 item
    • 113 |
    • level 2 item
        114 |
      • level 3 item
      • 115 |
      • level 3 item
      • 116 |
      117 |
    • 118 |
    119 |
  • 120 |
  • level 1 item
      121 |
    • level 2 item
    • 122 |
    • level 2 item
    • 123 |
    • level 2 item
    • 124 |
    125 |
  • 126 |
  • level 1 item
      127 |
    • level 2 item
    • 128 |
    • level 2 item
    • 129 |
    130 |
  • 131 |
  • level 1 item
  • 132 |
133 |

Small image

134 |

135 |

Large image

136 |

137 |

Definition lists can be used with HTML syntax.

138 |
139 |
Name
140 |
Godzilla
141 |
Born
142 |
1952
143 |
Birthplace
144 |
Japan
145 |
Color
146 |
Green
147 |
148 | 149 |
Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
150 | 
The final element.
151 | 
152 | 153 |
154 | This page was generated by NanoGen. 155 |
156 |
157 | 158 | 159 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nanogen", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "dev": true, 12 | "requires": { 13 | "mime-types": "~2.1.18", 14 | "negotiator": "0.6.1" 15 | } 16 | }, 17 | "address": { 18 | "version": "1.0.3", 19 | "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz", 20 | "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==", 21 | "dev": true 22 | }, 23 | "align-text": { 24 | "version": "0.1.4", 25 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 26 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 27 | "dev": true, 28 | "requires": { 29 | "kind-of": "^3.0.2", 30 | "longest": "^1.0.1", 31 | "repeat-string": "^1.5.2" 32 | } 33 | }, 34 | "amdefine": { 35 | "version": "1.0.1", 36 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 37 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 38 | "dev": true 39 | }, 40 | "ansi-align": { 41 | "version": "2.0.0", 42 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 43 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 44 | "dev": true, 45 | "requires": { 46 | "string-width": "^2.0.0" 47 | } 48 | }, 49 | "ansi-regex": { 50 | "version": "3.0.0", 51 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 52 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 53 | "dev": true 54 | }, 55 | "ansi-styles": { 56 | "version": "3.2.1", 57 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 58 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 59 | "dev": true, 60 | "requires": { 61 | "color-convert": "^1.9.0" 62 | } 63 | }, 64 | "arch": { 65 | "version": "2.1.0", 66 | "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.0.tgz", 67 | "integrity": "sha1-NhOqRhSQZLPB8GB5Gb8dR4boKIk=", 68 | "dev": true 69 | }, 70 | "argparse": { 71 | "version": "1.0.9", 72 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 73 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 74 | "dev": true, 75 | "requires": { 76 | "sprintf-js": "~1.0.2" 77 | } 78 | }, 79 | "args": { 80 | "version": "4.0.0", 81 | "resolved": "https://registry.npmjs.org/args/-/args-4.0.0.tgz", 82 | "integrity": "sha512-4b7lVF58nlo7sNtq8s2OueroOY/UHn0Nt/NVjsx9zn28u6yDVb9bQ/uy/5jKtHCbUDil4MlMyDLF5+OHEgnTug==", 83 | "dev": true, 84 | "requires": { 85 | "camelcase": "5.0.0", 86 | "chalk": "2.3.2", 87 | "leven": "2.1.0", 88 | "mri": "1.1.0" 89 | }, 90 | "dependencies": { 91 | "chalk": { 92 | "version": "2.3.2", 93 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", 94 | "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", 95 | "dev": true, 96 | "requires": { 97 | "ansi-styles": "^3.2.1", 98 | "escape-string-regexp": "^1.0.5", 99 | "supports-color": "^5.3.0" 100 | } 101 | } 102 | } 103 | }, 104 | "async": { 105 | "version": "1.5.2", 106 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 107 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 108 | "dev": true 109 | }, 110 | "balanced-match": { 111 | "version": "1.0.0", 112 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 113 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 114 | "dev": true 115 | }, 116 | "basic-auth": { 117 | "version": "2.0.0", 118 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", 119 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", 120 | "dev": true, 121 | "requires": { 122 | "safe-buffer": "5.1.1" 123 | } 124 | }, 125 | "bindings": { 126 | "version": "1.4.0", 127 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.4.0.tgz", 128 | "integrity": "sha512-7znEVX22Djn+nYjxCWKDne0RRloa9XfYa84yk3s+HkE3LpDYZmhArYr9O9huBoHY3/oXispx5LorIX7Sl2CgSQ==", 129 | "requires": { 130 | "file-uri-to-path": "1.0.0" 131 | } 132 | }, 133 | "bluebird": { 134 | "version": "3.5.1", 135 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 136 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", 137 | "dev": true 138 | }, 139 | "boxen": { 140 | "version": "1.3.0", 141 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 142 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 143 | "dev": true, 144 | "requires": { 145 | "ansi-align": "^2.0.0", 146 | "camelcase": "^4.0.0", 147 | "chalk": "^2.0.1", 148 | "cli-boxes": "^1.0.0", 149 | "string-width": "^2.0.0", 150 | "term-size": "^1.2.0", 151 | "widest-line": "^2.0.0" 152 | }, 153 | "dependencies": { 154 | "camelcase": { 155 | "version": "4.1.0", 156 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 157 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", 158 | "dev": true 159 | } 160 | } 161 | }, 162 | "brace-expansion": { 163 | "version": "1.1.8", 164 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 165 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 166 | "dev": true, 167 | "requires": { 168 | "balanced-match": "^1.0.0", 169 | "concat-map": "0.0.1" 170 | } 171 | }, 172 | "bytes": { 173 | "version": "3.0.0", 174 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 175 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", 176 | "dev": true 177 | }, 178 | "camelcase": { 179 | "version": "5.0.0", 180 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", 181 | "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", 182 | "dev": true 183 | }, 184 | "center-align": { 185 | "version": "0.1.3", 186 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 187 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 188 | "dev": true, 189 | "optional": true, 190 | "requires": { 191 | "align-text": "^0.1.3", 192 | "lazy-cache": "^1.0.3" 193 | } 194 | }, 195 | "chalk": { 196 | "version": "2.4.0", 197 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.0.tgz", 198 | "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", 199 | "dev": true, 200 | "requires": { 201 | "ansi-styles": "^3.2.1", 202 | "escape-string-regexp": "^1.0.5", 203 | "supports-color": "^5.3.0" 204 | } 205 | }, 206 | "cli-boxes": { 207 | "version": "1.0.0", 208 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 209 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", 210 | "dev": true 211 | }, 212 | "clipboardy": { 213 | "version": "1.2.3", 214 | "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", 215 | "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==", 216 | "dev": true, 217 | "requires": { 218 | "arch": "^2.1.0", 219 | "execa": "^0.8.0" 220 | }, 221 | "dependencies": { 222 | "execa": { 223 | "version": "0.8.0", 224 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", 225 | "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", 226 | "dev": true, 227 | "requires": { 228 | "cross-spawn": "^5.0.1", 229 | "get-stream": "^3.0.0", 230 | "is-stream": "^1.1.0", 231 | "npm-run-path": "^2.0.0", 232 | "p-finally": "^1.0.0", 233 | "signal-exit": "^3.0.0", 234 | "strip-eof": "^1.0.0" 235 | } 236 | } 237 | } 238 | }, 239 | "cliui": { 240 | "version": "2.1.0", 241 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 242 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 243 | "dev": true, 244 | "optional": true, 245 | "requires": { 246 | "center-align": "^0.1.1", 247 | "right-align": "^0.1.1", 248 | "wordwrap": "0.0.2" 249 | }, 250 | "dependencies": { 251 | "wordwrap": { 252 | "version": "0.0.2", 253 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 254 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 255 | "dev": true, 256 | "optional": true 257 | } 258 | } 259 | }, 260 | "color-convert": { 261 | "version": "1.9.1", 262 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 263 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 264 | "dev": true, 265 | "requires": { 266 | "color-name": "^1.1.1" 267 | } 268 | }, 269 | "color-name": { 270 | "version": "1.1.3", 271 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 272 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 273 | "dev": true 274 | }, 275 | "compressible": { 276 | "version": "2.0.13", 277 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", 278 | "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", 279 | "dev": true, 280 | "requires": { 281 | "mime-db": ">= 1.33.0 < 2" 282 | } 283 | }, 284 | "compression": { 285 | "version": "1.7.2", 286 | "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", 287 | "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", 288 | "dev": true, 289 | "requires": { 290 | "accepts": "~1.3.4", 291 | "bytes": "3.0.0", 292 | "compressible": "~2.0.13", 293 | "debug": "2.6.9", 294 | "on-headers": "~1.0.1", 295 | "safe-buffer": "5.1.1", 296 | "vary": "~1.1.2" 297 | } 298 | }, 299 | "concat-map": { 300 | "version": "0.0.1", 301 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 302 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 303 | "dev": true 304 | }, 305 | "content-type": { 306 | "version": "1.0.4", 307 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 308 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 309 | "dev": true 310 | }, 311 | "cross-env": { 312 | "version": "5.1.4", 313 | "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.4.tgz", 314 | "integrity": "sha512-Mx8mw6JWhfpYoEk7PGvHxJMLQwQHORAs8+2bX+C1lGQ4h3GkDb1zbzC2Nw85YH9ZQMlO0BHZxMacgrfPmMFxbg==", 315 | "dev": true, 316 | "requires": { 317 | "cross-spawn": "^5.1.0", 318 | "is-windows": "^1.0.0" 319 | } 320 | }, 321 | "cross-spawn": { 322 | "version": "5.1.0", 323 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 324 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 325 | "dev": true, 326 | "requires": { 327 | "lru-cache": "^4.0.1", 328 | "shebang-command": "^1.2.0", 329 | "which": "^1.2.9" 330 | } 331 | }, 332 | "dargs": { 333 | "version": "5.1.0", 334 | "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", 335 | "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", 336 | "dev": true 337 | }, 338 | "debug": { 339 | "version": "2.6.9", 340 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 341 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 342 | "dev": true, 343 | "requires": { 344 | "ms": "2.0.0" 345 | } 346 | }, 347 | "decamelize": { 348 | "version": "1.2.0", 349 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 350 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 351 | "dev": true, 352 | "optional": true 353 | }, 354 | "deep-extend": { 355 | "version": "0.4.2", 356 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", 357 | "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", 358 | "dev": true 359 | }, 360 | "depd": { 361 | "version": "1.1.1", 362 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 363 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", 364 | "dev": true 365 | }, 366 | "destroy": { 367 | "version": "1.0.4", 368 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 369 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 370 | "dev": true 371 | }, 372 | "detect-port": { 373 | "version": "1.2.2", 374 | "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.2.2.tgz", 375 | "integrity": "sha512-06H99JMCwgbYbA+codm97aBhFLAjABftetp+v+Z88Pvvlkawp2N+1bP/9J24+mihrvk9yBvUYTyIj3NixG1CsA==", 376 | "dev": true, 377 | "requires": { 378 | "address": "^1.0.1", 379 | "debug": "^2.6.0" 380 | } 381 | }, 382 | "ee-first": { 383 | "version": "1.1.1", 384 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 385 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 386 | "dev": true 387 | }, 388 | "ejs": { 389 | "version": "2.5.9", 390 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", 391 | "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==", 392 | "dev": true 393 | }, 394 | "encodeurl": { 395 | "version": "1.0.2", 396 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 397 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 398 | "dev": true 399 | }, 400 | "escape-html": { 401 | "version": "1.0.3", 402 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 403 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 404 | "dev": true 405 | }, 406 | "escape-string-regexp": { 407 | "version": "1.0.5", 408 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 409 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 410 | "dev": true 411 | }, 412 | "esprima": { 413 | "version": "4.0.0", 414 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 415 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", 416 | "dev": true 417 | }, 418 | "etag": { 419 | "version": "1.8.1", 420 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 421 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 422 | "dev": true 423 | }, 424 | "execa": { 425 | "version": "0.7.0", 426 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 427 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 428 | "dev": true, 429 | "requires": { 430 | "cross-spawn": "^5.0.1", 431 | "get-stream": "^3.0.0", 432 | "is-stream": "^1.1.0", 433 | "npm-run-path": "^2.0.0", 434 | "p-finally": "^1.0.0", 435 | "signal-exit": "^3.0.0", 436 | "strip-eof": "^1.0.0" 437 | } 438 | }, 439 | "file-uri-to-path": { 440 | "version": "1.0.0", 441 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 442 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 443 | }, 444 | "filesize": { 445 | "version": "3.6.1", 446 | "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", 447 | "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", 448 | "dev": true 449 | }, 450 | "fresh": { 451 | "version": "0.5.2", 452 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 453 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 454 | "dev": true 455 | }, 456 | "front-matter": { 457 | "version": "2.3.0", 458 | "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-2.3.0.tgz", 459 | "integrity": "sha1-cgOviWzjV+4E4qpFFp6pHtf2dQQ=", 460 | "dev": true, 461 | "requires": { 462 | "js-yaml": "^3.10.0" 463 | } 464 | }, 465 | "fs-extra": { 466 | "version": "5.0.0", 467 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", 468 | "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", 469 | "dev": true, 470 | "requires": { 471 | "graceful-fs": "^4.1.2", 472 | "jsonfile": "^4.0.0", 473 | "universalify": "^0.1.0" 474 | } 475 | }, 476 | "fs.realpath": { 477 | "version": "1.0.0", 478 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 479 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 480 | "dev": true 481 | }, 482 | "get-stream": { 483 | "version": "3.0.0", 484 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 485 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 486 | "dev": true 487 | }, 488 | "glob": { 489 | "version": "7.1.2", 490 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 491 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 492 | "dev": true, 493 | "requires": { 494 | "fs.realpath": "^1.0.0", 495 | "inflight": "^1.0.4", 496 | "inherits": "2", 497 | "minimatch": "^3.0.4", 498 | "once": "^1.3.0", 499 | "path-is-absolute": "^1.0.0" 500 | } 501 | }, 502 | "graceful-fs": { 503 | "version": "4.1.11", 504 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 505 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 506 | "dev": true 507 | }, 508 | "handlebars": { 509 | "version": "4.0.11", 510 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", 511 | "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", 512 | "dev": true, 513 | "requires": { 514 | "async": "^1.4.0", 515 | "optimist": "^0.6.1", 516 | "source-map": "^0.4.4", 517 | "uglify-js": "^2.6" 518 | } 519 | }, 520 | "has-flag": { 521 | "version": "3.0.0", 522 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 523 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 524 | "dev": true 525 | }, 526 | "hoek": { 527 | "version": "4.2.1", 528 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", 529 | "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" 530 | }, 531 | "http-errors": { 532 | "version": "1.6.2", 533 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 534 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 535 | "dev": true, 536 | "requires": { 537 | "depd": "1.1.1", 538 | "inherits": "2.0.3", 539 | "setprototypeof": "1.0.3", 540 | "statuses": ">= 1.3.1 < 2" 541 | } 542 | }, 543 | "iconv-lite": { 544 | "version": "0.4.19", 545 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 546 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", 547 | "dev": true 548 | }, 549 | "inflight": { 550 | "version": "1.0.6", 551 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 552 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 553 | "dev": true, 554 | "requires": { 555 | "once": "^1.3.0", 556 | "wrappy": "1" 557 | } 558 | }, 559 | "inherits": { 560 | "version": "2.0.3", 561 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 562 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 563 | "dev": true 564 | }, 565 | "ini": { 566 | "version": "1.3.5", 567 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 568 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", 569 | "dev": true 570 | }, 571 | "ip": { 572 | "version": "1.1.5", 573 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 574 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", 575 | "dev": true 576 | }, 577 | "is-buffer": { 578 | "version": "1.1.6", 579 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 580 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", 581 | "dev": true 582 | }, 583 | "is-fullwidth-code-point": { 584 | "version": "2.0.0", 585 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 586 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 587 | "dev": true 588 | }, 589 | "is-stream": { 590 | "version": "1.1.0", 591 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 592 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 593 | "dev": true 594 | }, 595 | "is-windows": { 596 | "version": "1.0.2", 597 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 598 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 599 | "dev": true 600 | }, 601 | "is-wsl": { 602 | "version": "1.1.0", 603 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", 604 | "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", 605 | "dev": true 606 | }, 607 | "isemail": { 608 | "version": "3.2.0", 609 | "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", 610 | "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", 611 | "requires": { 612 | "punycode": "2.x.x" 613 | } 614 | }, 615 | "isexe": { 616 | "version": "2.0.0", 617 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 618 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 619 | "dev": true 620 | }, 621 | "joi": { 622 | "version": "13.7.0", 623 | "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", 624 | "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", 625 | "requires": { 626 | "hoek": "5.x.x", 627 | "isemail": "3.x.x", 628 | "topo": "3.x.x" 629 | }, 630 | "dependencies": { 631 | "hoek": { 632 | "version": "5.0.4", 633 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", 634 | "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" 635 | } 636 | } 637 | }, 638 | "js-yaml": { 639 | "version": "3.10.0", 640 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", 641 | "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", 642 | "dev": true, 643 | "requires": { 644 | "argparse": "^1.0.7", 645 | "esprima": "^4.0.0" 646 | } 647 | }, 648 | "jsonfile": { 649 | "version": "4.0.0", 650 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 651 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 652 | "dev": true, 653 | "requires": { 654 | "graceful-fs": "^4.1.6" 655 | } 656 | }, 657 | "kind-of": { 658 | "version": "3.2.2", 659 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 660 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 661 | "dev": true, 662 | "requires": { 663 | "is-buffer": "^1.1.5" 664 | } 665 | }, 666 | "lazy-cache": { 667 | "version": "1.0.4", 668 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 669 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 670 | "dev": true, 671 | "optional": true 672 | }, 673 | "leven": { 674 | "version": "2.1.0", 675 | "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", 676 | "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", 677 | "dev": true 678 | }, 679 | "longest": { 680 | "version": "1.0.1", 681 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 682 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 683 | "dev": true 684 | }, 685 | "lru-cache": { 686 | "version": "4.1.2", 687 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", 688 | "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", 689 | "dev": true, 690 | "requires": { 691 | "pseudomap": "^1.0.2", 692 | "yallist": "^2.1.2" 693 | } 694 | }, 695 | "marked": { 696 | "version": "0.3.19", 697 | "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", 698 | "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", 699 | "dev": true 700 | }, 701 | "micro": { 702 | "version": "9.1.4", 703 | "resolved": "https://registry.npmjs.org/micro/-/micro-9.1.4.tgz", 704 | "integrity": "sha512-2eQ5QpO33LAPQJ/1iA3BrtLzdYlOCAXlstY4vcIj3SEmv0tsWuNZpsfgn1o8xGAYzf15kb+FXnEx8EklXz+pPg==", 705 | "dev": true, 706 | "requires": { 707 | "content-type": "1.0.4", 708 | "is-stream": "1.1.0", 709 | "mri": "1.1.0", 710 | "raw-body": "2.3.2" 711 | } 712 | }, 713 | "micro-compress": { 714 | "version": "1.0.0", 715 | "resolved": "https://registry.npmjs.org/micro-compress/-/micro-compress-1.0.0.tgz", 716 | "integrity": "sha1-U/WoC0rQMgyhZaVZtuPfFF1PcE8=", 717 | "dev": true, 718 | "requires": { 719 | "compression": "^1.6.2" 720 | } 721 | }, 722 | "mime": { 723 | "version": "1.4.1", 724 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 725 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", 726 | "dev": true 727 | }, 728 | "mime-db": { 729 | "version": "1.33.0", 730 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 731 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", 732 | "dev": true 733 | }, 734 | "mime-types": { 735 | "version": "2.1.18", 736 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 737 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 738 | "dev": true, 739 | "requires": { 740 | "mime-db": "~1.33.0" 741 | } 742 | }, 743 | "minimatch": { 744 | "version": "3.0.4", 745 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 746 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 747 | "dev": true, 748 | "requires": { 749 | "brace-expansion": "^1.1.7" 750 | } 751 | }, 752 | "minimist": { 753 | "version": "0.0.10", 754 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 755 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 756 | "dev": true 757 | }, 758 | "mri": { 759 | "version": "1.1.0", 760 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.0.tgz", 761 | "integrity": "sha1-XAo/KcjM/7ux7JQdzsCdcfoy82o=", 762 | "dev": true 763 | }, 764 | "ms": { 765 | "version": "2.0.0", 766 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 767 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 768 | "dev": true 769 | }, 770 | "nan": { 771 | "version": "2.12.1", 772 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", 773 | "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" 774 | }, 775 | "negotiator": { 776 | "version": "0.6.1", 777 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 778 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", 779 | "dev": true 780 | }, 781 | "node-expat": { 782 | "version": "2.3.17", 783 | "resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.17.tgz", 784 | "integrity": "sha512-mNTxY/GMiZGayqdKZXyf6lJR7OM1JqyL0EISjE4XF7Ov7+X4zJjmlnfxCi6Gml90IEOyiYBcyJg9MHDsDp6YHw==", 785 | "requires": { 786 | "bindings": "^1.2.1", 787 | "nan": "^2.10.0" 788 | } 789 | }, 790 | "node-version": { 791 | "version": "1.1.3", 792 | "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.1.3.tgz", 793 | "integrity": "sha512-rEwE51JWn0yN3Wl5BXeGn5d52OGbSXzWiiXRjAQeuyvcGKyvuSILW2rb3G7Xh+nexzLwhTpek6Ehxd6IjvHePg==", 794 | "dev": true 795 | }, 796 | "npm-run-path": { 797 | "version": "2.0.2", 798 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 799 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 800 | "dev": true, 801 | "requires": { 802 | "path-key": "^2.0.0" 803 | } 804 | }, 805 | "on-finished": { 806 | "version": "2.3.0", 807 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 808 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 809 | "dev": true, 810 | "requires": { 811 | "ee-first": "1.1.1" 812 | } 813 | }, 814 | "on-headers": { 815 | "version": "1.0.1", 816 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", 817 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", 818 | "dev": true 819 | }, 820 | "once": { 821 | "version": "1.4.0", 822 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 823 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 824 | "dev": true, 825 | "requires": { 826 | "wrappy": "1" 827 | } 828 | }, 829 | "openssl-self-signed-certificate": { 830 | "version": "1.1.6", 831 | "resolved": "https://registry.npmjs.org/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz", 832 | "integrity": "sha1-nTpHdrGlfphHNQOSEUrS+RWoPdQ=", 833 | "dev": true 834 | }, 835 | "opn": { 836 | "version": "5.3.0", 837 | "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", 838 | "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", 839 | "dev": true, 840 | "requires": { 841 | "is-wsl": "^1.1.0" 842 | } 843 | }, 844 | "optimist": { 845 | "version": "0.6.1", 846 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 847 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 848 | "dev": true, 849 | "requires": { 850 | "minimist": "~0.0.1", 851 | "wordwrap": "~0.0.2" 852 | } 853 | }, 854 | "p-finally": { 855 | "version": "1.0.0", 856 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 857 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 858 | "dev": true 859 | }, 860 | "path-is-absolute": { 861 | "version": "1.0.1", 862 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 863 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 864 | "dev": true 865 | }, 866 | "path-is-inside": { 867 | "version": "1.0.2", 868 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 869 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 870 | "dev": true 871 | }, 872 | "path-key": { 873 | "version": "2.0.1", 874 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 875 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 876 | "dev": true 877 | }, 878 | "path-type": { 879 | "version": "3.0.0", 880 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 881 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 882 | "dev": true, 883 | "requires": { 884 | "pify": "^3.0.0" 885 | } 886 | }, 887 | "pify": { 888 | "version": "3.0.0", 889 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 890 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 891 | "dev": true 892 | }, 893 | "prettier": { 894 | "version": "1.12.1", 895 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.12.1.tgz", 896 | "integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU=", 897 | "dev": true 898 | }, 899 | "pseudomap": { 900 | "version": "1.0.2", 901 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 902 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 903 | "dev": true 904 | }, 905 | "punycode": { 906 | "version": "2.1.1", 907 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 908 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 909 | }, 910 | "range-parser": { 911 | "version": "1.2.0", 912 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 913 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 914 | "dev": true 915 | }, 916 | "raw-body": { 917 | "version": "2.3.2", 918 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 919 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 920 | "dev": true, 921 | "requires": { 922 | "bytes": "3.0.0", 923 | "http-errors": "1.6.2", 924 | "iconv-lite": "0.4.19", 925 | "unpipe": "1.0.0" 926 | } 927 | }, 928 | "rc": { 929 | "version": "1.2.6", 930 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", 931 | "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", 932 | "dev": true, 933 | "requires": { 934 | "deep-extend": "~0.4.0", 935 | "ini": "~1.3.0", 936 | "minimist": "^1.2.0", 937 | "strip-json-comments": "~2.0.1" 938 | }, 939 | "dependencies": { 940 | "minimist": { 941 | "version": "1.2.0", 942 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 943 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 944 | "dev": true 945 | } 946 | } 947 | }, 948 | "registry-auth-token": { 949 | "version": "3.3.2", 950 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 951 | "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 952 | "dev": true, 953 | "requires": { 954 | "rc": "^1.1.6", 955 | "safe-buffer": "^5.0.1" 956 | } 957 | }, 958 | "registry-url": { 959 | "version": "3.1.0", 960 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 961 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 962 | "dev": true, 963 | "requires": { 964 | "rc": "^1.0.1" 965 | } 966 | }, 967 | "repeat-string": { 968 | "version": "1.6.1", 969 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 970 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 971 | "dev": true 972 | }, 973 | "right-align": { 974 | "version": "0.1.3", 975 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 976 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 977 | "dev": true, 978 | "optional": true, 979 | "requires": { 980 | "align-text": "^0.1.1" 981 | } 982 | }, 983 | "safe-buffer": { 984 | "version": "5.1.1", 985 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 986 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", 987 | "dev": true 988 | }, 989 | "send": { 990 | "version": "0.16.2", 991 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 992 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 993 | "dev": true, 994 | "requires": { 995 | "debug": "2.6.9", 996 | "depd": "~1.1.2", 997 | "destroy": "~1.0.4", 998 | "encodeurl": "~1.0.2", 999 | "escape-html": "~1.0.3", 1000 | "etag": "~1.8.1", 1001 | "fresh": "0.5.2", 1002 | "http-errors": "~1.6.2", 1003 | "mime": "1.4.1", 1004 | "ms": "2.0.0", 1005 | "on-finished": "~2.3.0", 1006 | "range-parser": "~1.2.0", 1007 | "statuses": "~1.4.0" 1008 | }, 1009 | "dependencies": { 1010 | "depd": { 1011 | "version": "1.1.2", 1012 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 1013 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 1014 | "dev": true 1015 | }, 1016 | "statuses": { 1017 | "version": "1.4.0", 1018 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1019 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", 1020 | "dev": true 1021 | } 1022 | } 1023 | }, 1024 | "serve": { 1025 | "version": "6.5.6", 1026 | "resolved": "https://registry.npmjs.org/serve/-/serve-6.5.6.tgz", 1027 | "integrity": "sha512-rdbHRzBh94UdZPktcoNjWEUqY0ILanZ8Q+VwNfhw5n93pkCPn86wH8x+hXgRi8II9+xj3DvGMWL/6hGUQyfuXg==", 1028 | "dev": true, 1029 | "requires": { 1030 | "args": "4.0.0", 1031 | "basic-auth": "2.0.0", 1032 | "bluebird": "3.5.1", 1033 | "boxen": "1.3.0", 1034 | "chalk": "2.4.0", 1035 | "clipboardy": "1.2.3", 1036 | "dargs": "5.1.0", 1037 | "detect-port": "1.2.2", 1038 | "filesize": "3.6.1", 1039 | "fs-extra": "5.0.0", 1040 | "handlebars": "4.0.11", 1041 | "ip": "1.1.5", 1042 | "micro": "9.1.4", 1043 | "micro-compress": "1.0.0", 1044 | "mime-types": "2.1.18", 1045 | "node-version": "1.1.3", 1046 | "openssl-self-signed-certificate": "1.1.6", 1047 | "opn": "5.3.0", 1048 | "path-is-inside": "1.0.2", 1049 | "path-type": "3.0.0", 1050 | "send": "0.16.2", 1051 | "update-check": "1.3.2" 1052 | } 1053 | }, 1054 | "setprototypeof": { 1055 | "version": "1.0.3", 1056 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 1057 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", 1058 | "dev": true 1059 | }, 1060 | "shebang-command": { 1061 | "version": "1.2.0", 1062 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1063 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1064 | "dev": true, 1065 | "requires": { 1066 | "shebang-regex": "^1.0.0" 1067 | } 1068 | }, 1069 | "shebang-regex": { 1070 | "version": "1.0.0", 1071 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1072 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1073 | "dev": true 1074 | }, 1075 | "signal-exit": { 1076 | "version": "3.0.2", 1077 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1078 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1079 | "dev": true 1080 | }, 1081 | "source-map": { 1082 | "version": "0.4.4", 1083 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", 1084 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", 1085 | "dev": true, 1086 | "requires": { 1087 | "amdefine": ">=0.0.4" 1088 | } 1089 | }, 1090 | "sprintf-js": { 1091 | "version": "1.0.3", 1092 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1093 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1094 | "dev": true 1095 | }, 1096 | "statuses": { 1097 | "version": "1.5.0", 1098 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1099 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 1100 | "dev": true 1101 | }, 1102 | "string-width": { 1103 | "version": "2.1.1", 1104 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1105 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1106 | "dev": true, 1107 | "requires": { 1108 | "is-fullwidth-code-point": "^2.0.0", 1109 | "strip-ansi": "^4.0.0" 1110 | } 1111 | }, 1112 | "strip-ansi": { 1113 | "version": "4.0.0", 1114 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1115 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1116 | "dev": true, 1117 | "requires": { 1118 | "ansi-regex": "^3.0.0" 1119 | } 1120 | }, 1121 | "strip-eof": { 1122 | "version": "1.0.0", 1123 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1124 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 1125 | "dev": true 1126 | }, 1127 | "strip-json-comments": { 1128 | "version": "2.0.1", 1129 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1130 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1131 | "dev": true 1132 | }, 1133 | "supports-color": { 1134 | "version": "5.4.0", 1135 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 1136 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 1137 | "dev": true, 1138 | "requires": { 1139 | "has-flag": "^3.0.0" 1140 | } 1141 | }, 1142 | "term-size": { 1143 | "version": "1.2.0", 1144 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 1145 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 1146 | "dev": true, 1147 | "requires": { 1148 | "execa": "^0.7.0" 1149 | } 1150 | }, 1151 | "topo": { 1152 | "version": "3.0.3", 1153 | "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", 1154 | "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", 1155 | "requires": { 1156 | "hoek": "6.x.x" 1157 | }, 1158 | "dependencies": { 1159 | "hoek": { 1160 | "version": "6.1.2", 1161 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.2.tgz", 1162 | "integrity": "sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q==" 1163 | } 1164 | } 1165 | }, 1166 | "uglify-js": { 1167 | "version": "2.8.29", 1168 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 1169 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 1170 | "dev": true, 1171 | "optional": true, 1172 | "requires": { 1173 | "source-map": "~0.5.1", 1174 | "uglify-to-browserify": "~1.0.0", 1175 | "yargs": "~3.10.0" 1176 | }, 1177 | "dependencies": { 1178 | "source-map": { 1179 | "version": "0.5.7", 1180 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1181 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1182 | "dev": true, 1183 | "optional": true 1184 | } 1185 | } 1186 | }, 1187 | "uglify-to-browserify": { 1188 | "version": "1.0.2", 1189 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 1190 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 1191 | "dev": true, 1192 | "optional": true 1193 | }, 1194 | "universalify": { 1195 | "version": "0.1.1", 1196 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", 1197 | "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", 1198 | "dev": true 1199 | }, 1200 | "unpipe": { 1201 | "version": "1.0.0", 1202 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1203 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1204 | "dev": true 1205 | }, 1206 | "update-check": { 1207 | "version": "1.3.2", 1208 | "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.3.2.tgz", 1209 | "integrity": "sha512-0iGt63gXrsU4VTw4tIGVVk14H6KLHI7ExNPuSmdDdwUrUAQTBnh1hQcRpnoBWetb3/Ab4YyXK1iDWXP7D0VHTQ==", 1210 | "dev": true, 1211 | "requires": { 1212 | "registry-auth-token": "3.3.2", 1213 | "registry-url": "3.1.0" 1214 | } 1215 | }, 1216 | "vary": { 1217 | "version": "1.1.2", 1218 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1219 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 1220 | "dev": true 1221 | }, 1222 | "which": { 1223 | "version": "1.3.0", 1224 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 1225 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 1226 | "dev": true, 1227 | "requires": { 1228 | "isexe": "^2.0.0" 1229 | } 1230 | }, 1231 | "widest-line": { 1232 | "version": "2.0.0", 1233 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", 1234 | "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", 1235 | "dev": true, 1236 | "requires": { 1237 | "string-width": "^2.1.1" 1238 | } 1239 | }, 1240 | "window-size": { 1241 | "version": "0.1.0", 1242 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 1243 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 1244 | "dev": true, 1245 | "optional": true 1246 | }, 1247 | "wordwrap": { 1248 | "version": "0.0.3", 1249 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 1250 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 1251 | "dev": true 1252 | }, 1253 | "wrappy": { 1254 | "version": "1.0.2", 1255 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1256 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1257 | "dev": true 1258 | }, 1259 | "xml2json": { 1260 | "version": "0.11.2", 1261 | "resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.11.2.tgz", 1262 | "integrity": "sha512-ZJpHpPOL0T5lOvAHMnWm59iQOPqNtam5t2TMUllWZ1k5Wm8L5YyvQnkeaVnRKCvDwY5EumqXWyOjjMdQVz272A==", 1263 | "requires": { 1264 | "hoek": "^4.2.1", 1265 | "joi": "^13.1.2", 1266 | "node-expat": "^2.3.15" 1267 | } 1268 | }, 1269 | "yallist": { 1270 | "version": "2.1.2", 1271 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1272 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 1273 | "dev": true 1274 | }, 1275 | "yargs": { 1276 | "version": "3.10.0", 1277 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 1278 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 1279 | "dev": true, 1280 | "optional": true, 1281 | "requires": { 1282 | "camelcase": "^1.0.2", 1283 | "cliui": "^2.1.0", 1284 | "decamelize": "^1.0.0", 1285 | "window-size": "0.1.0" 1286 | }, 1287 | "dependencies": { 1288 | "camelcase": { 1289 | "version": "1.2.1", 1290 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 1291 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 1292 | "dev": true, 1293 | "optional": true 1294 | } 1295 | } 1296 | } 1297 | } 1298 | } 1299 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nanogen", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node ./scripts/build", 8 | "build:prod": "cross-env NODE_ENV=production node ./scripts/build", 9 | "serve": "serve ./public" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "xml2json": "^0.11.2" 16 | }, 17 | "devDependencies": { 18 | "cross-env": "^5.1.4", 19 | "ejs": "^2.5.9", 20 | "front-matter": "^2.3.0", 21 | "fs-extra": "^5.0.0", 22 | "glob": "^7.1.2", 23 | "marked": "^0.3.19", 24 | "prettier": "^1.12.1", 25 | "serve": "^6.5.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/scripts/build.js: -------------------------------------------------------------------------------- 1 | const fse = require('fs-extra'); 2 | const path = require('path'); 3 | const ejs = require('ejs'); 4 | const marked = require('marked'); 5 | const frontMatter = require('front-matter'); 6 | const glob = require('glob'); 7 | const config = require('../site.config'); 8 | 9 | const srcPath = './src'; 10 | const distPath = config.build.outputPath; 11 | 12 | // clear destination folder 13 | fse.emptyDirSync(distPath); 14 | 15 | // copy assets folder 16 | fse.copy(`${srcPath}/assets`, `${distPath}/assets`); 17 | 18 | // read pages 19 | const files = glob.sync('**/*.@(md|ejs|html)', { cwd: `${srcPath}/pages` }); 20 | 21 | files.forEach((file, i) => { 22 | const fileData = path.parse(file); 23 | const destPath = path.join(distPath, fileData.dir); 24 | 25 | // create destination directory 26 | fse.mkdirsSync(destPath); 27 | 28 | // read page file 29 | const data = fse.readFileSync(`${srcPath}/pages/${file}`, 'utf-8'); 30 | 31 | // render page 32 | const pageData = frontMatter(data); 33 | const templateConfig = Object.assign({}, config, { 34 | page: pageData.attributes 35 | }); 36 | let pageContent; 37 | 38 | // generate page content according to file type 39 | switch (fileData.ext) { 40 | case '.md': 41 | pageContent = marked(pageData.body); 42 | break; 43 | case '.ejs': 44 | pageContent = ejs.render(pageData.body, templateConfig, { 45 | filename: `${srcPath}/pages/${file}` 46 | }); 47 | break; 48 | default: 49 | pageContent = pageData.body; 50 | } 51 | 52 | // render layout with page contents 53 | const layout = pageData.attributes.layout || 'default'; 54 | const layoutFileName = `${srcPath}/layouts/${layout}.ejs`; 55 | const layoutData = fse.readFileSync(layoutFileName, 'utf-8'); 56 | const completePage = ejs.render( 57 | layoutData, 58 | Object.assign({}, templateConfig, { 59 | body: pageContent, 60 | filename: layoutFileName 61 | }) 62 | ); 63 | 64 | // save the html file 65 | fse.writeFileSync(`${destPath}/${fileData.name}.html`, completePage); 66 | }); 67 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/site.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const projects = []; 3 | const xml2json = require('xml2json'); 4 | 5 | function load_content(file, default_ret) { 6 | default_ret = default_ret || {} 7 | try { 8 | let ret = require(file) 9 | return ret 10 | } catch(e) { 11 | console.error(`Failed to load ${file}:${e}`) 12 | return default_ret 13 | } 14 | } 15 | 16 | const hosts = load_content('./src/data/crtsh/data'); 17 | 18 | let zapPath = './src/data/owasp-zap' 19 | const zapScans = {} 20 | 21 | fs.readdirSync(zapPath).forEach(function (file) { 22 | console.log('Reading file: ' + file) 23 | let x = load_content(zapPath + '/' + file) 24 | 25 | // TBD: Fix issue with multiple site in a file 26 | 27 | zapScans[x.site["@name"]] = x 28 | console.log("---") 29 | // console.log(x) 30 | }) 31 | 32 | // Read NMAP XML and transform to JSON 33 | let nmap = null 34 | try { 35 | const nmapXml = fs.readFileSync('./src/data/nmap/data.xml') 36 | nmap = xml2json.toJson(nmapXml) 37 | } catch(e) { 38 | nmap = {} 39 | } 40 | 41 | module.exports = { 42 | site: { 43 | title: 'SPLAT Workshop | nullcon X', 44 | description: 'Automated Scan Report', 45 | basePath: process.env.NODE_ENV === 'production' ? '/nanogen' : '', 46 | projects, 47 | hosts, 48 | zapScans, 49 | nmap 50 | }, 51 | build: { 52 | outputPath: process.env.NODE_ENV === 'production' ? './docs' : './public' 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | html, body { 3 | font-family: sans-serif; 4 | -ms-text-size-adjust: 100%; 5 | -webkit-text-size-adjust: 100%; 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | 11 | article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { 12 | display: block 13 | } 14 | 15 | footer { 16 | position: relative; 17 | bottom: 0; 18 | width: 100%; 19 | } 20 | 21 | h2 { 22 | color: #1e6bb8; 23 | font-size: 2rem; 24 | font-weight: 600; 25 | line-height: 1.125; 26 | } 27 | .subtitle{ 28 | border: 1px dashed #efefef; 29 | padding: 5px 5px; 30 | background: whitesmoke; 31 | font-weight: bolder; 32 | } 33 | .scan-heading{ 34 | border-left: 15px solid #1e6bb8; 35 | border-bottom: 2px solid #000; 36 | padding: 20px 20px; 37 | } 38 | .list { 39 | background-color: #fff; 40 | border-radius: 4px; 41 | box-shadow: 0 2px 3px rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.1); 42 | } 43 | 44 | .list-item:not(:last-child) { 45 | border-bottom: 1px solid #dbdbdb; 46 | } 47 | .list-item { 48 | display: block; 49 | padding: .5em 1em; 50 | } 51 | 52 | audio, canvas, progress, video { 53 | display: inline-block; 54 | vertical-align: baseline 55 | } 56 | 57 | audio:not([controls]) { 58 | display: none; 59 | height: 0 60 | } 61 | 62 | [hidden], template { 63 | display: none 64 | } 65 | 66 | a { 67 | background-color: transparent 68 | } 69 | 70 | a:active, a:hover { 71 | outline: 0 72 | } 73 | 74 | abbr[title] { 75 | border-bottom: 1px dotted 76 | } 77 | 78 | b, strong { 79 | font-weight: bold 80 | } 81 | 82 | dfn { 83 | font-style: italic 84 | } 85 | 86 | h1 { 87 | font-size: 2em; 88 | margin: 0.67em 0 89 | } 90 | 91 | mark { 92 | background: #ff0; 93 | color: #000 94 | } 95 | 96 | small { 97 | font-size: 80% 98 | } 99 | 100 | sub, sup { 101 | font-size: 75%; 102 | line-height: 0; 103 | position: relative; 104 | vertical-align: baseline 105 | } 106 | 107 | sup { 108 | top: -0.5em 109 | } 110 | 111 | sub { 112 | bottom: -0.25em 113 | } 114 | 115 | img { 116 | border: 0 117 | } 118 | 119 | svg:not(:root) { 120 | overflow: hidden 121 | } 122 | 123 | figure { 124 | margin: 1em 40px 125 | } 126 | 127 | hr { 128 | box-sizing: content-box; 129 | height: 0 130 | } 131 | 132 | pre { 133 | overflow: auto 134 | } 135 | 136 | code, kbd, pre, samp { 137 | font-family: monospace, monospace; 138 | font-size: 1em 139 | } 140 | 141 | button, input, optgroup, select, textarea { 142 | color: inherit; 143 | font: inherit; 144 | margin: 0 145 | } 146 | 147 | button { 148 | overflow: visible 149 | } 150 | 151 | button, select { 152 | text-transform: none 153 | } 154 | 155 | button, html input[type="button"], input[type="reset"], input[type="submit"] { 156 | -webkit-appearance: button; 157 | cursor: pointer 158 | } 159 | 160 | button[disabled], html input[disabled] { 161 | cursor: default 162 | } 163 | 164 | button::-moz-focus-inner, input::-moz-focus-inner { 165 | border: 0; 166 | padding: 0 167 | } 168 | 169 | input { 170 | line-height: normal 171 | } 172 | 173 | input[type="checkbox"], input[type="radio"] { 174 | box-sizing: border-box; 175 | padding: 0 176 | } 177 | 178 | input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { 179 | height: auto 180 | } 181 | 182 | input[type="search"] { 183 | -webkit-appearance: textfield; 184 | box-sizing: content-box 185 | } 186 | 187 | input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { 188 | -webkit-appearance: none 189 | } 190 | 191 | fieldset { 192 | border: 1px solid #c0c0c0; 193 | margin: 0 2px; 194 | padding: 0.35em 0.625em 0.75em 195 | } 196 | 197 | legend { 198 | border: 0; 199 | padding: 0 200 | } 201 | 202 | textarea { 203 | overflow: auto 204 | } 205 | 206 | optgroup { 207 | font-weight: bold 208 | } 209 | 210 | table { 211 | border-collapse: collapse; 212 | border-spacing: 0 213 | } 214 | 215 | td, th { 216 | padding: 0 217 | } 218 | 219 | .highlight table td { 220 | padding: 5px 221 | } 222 | 223 | .highlight table pre { 224 | margin: 0 225 | } 226 | 227 | .highlight .cm { 228 | color: #999988; 229 | font-style: italic 230 | } 231 | 232 | .highlight .cp { 233 | color: #999999; 234 | font-weight: bold 235 | } 236 | 237 | .highlight .c1 { 238 | color: #999988; 239 | font-style: italic 240 | } 241 | 242 | .highlight .cs { 243 | color: #999999; 244 | font-weight: bold; 245 | font-style: italic 246 | } 247 | 248 | .highlight .c, .highlight .cd { 249 | color: #999988; 250 | font-style: italic 251 | } 252 | 253 | .highlight .err { 254 | color: #a61717; 255 | background-color: #e3d2d2 256 | } 257 | 258 | .highlight .gd { 259 | color: #000000; 260 | background-color: #ffdddd 261 | } 262 | 263 | .highlight .ge { 264 | color: #000000; 265 | font-style: italic 266 | } 267 | 268 | .highlight .gr { 269 | color: #aa0000 270 | } 271 | 272 | .highlight .gh { 273 | color: #999999 274 | } 275 | 276 | .highlight .gi { 277 | color: #000000; 278 | background-color: #ddffdd 279 | } 280 | 281 | .highlight .go { 282 | color: #888888 283 | } 284 | 285 | .highlight .gp { 286 | color: #555555 287 | } 288 | 289 | .highlight .gs { 290 | font-weight: bold 291 | } 292 | 293 | .highlight .gu { 294 | color: #aaaaaa 295 | } 296 | 297 | .highlight .gt { 298 | color: #aa0000 299 | } 300 | 301 | .highlight .kc { 302 | color: #000000; 303 | font-weight: bold 304 | } 305 | 306 | .highlight .kd { 307 | color: #000000; 308 | font-weight: bold 309 | } 310 | 311 | .highlight .kn { 312 | color: #000000; 313 | font-weight: bold 314 | } 315 | 316 | .highlight .kp { 317 | color: #000000; 318 | font-weight: bold 319 | } 320 | 321 | .highlight .kr { 322 | color: #000000; 323 | font-weight: bold 324 | } 325 | 326 | .highlight .kt { 327 | color: #445588; 328 | font-weight: bold 329 | } 330 | 331 | .highlight .k, .highlight .kv { 332 | color: #000000; 333 | font-weight: bold 334 | } 335 | 336 | .highlight .mf { 337 | color: #009999 338 | } 339 | 340 | .highlight .mh { 341 | color: #009999 342 | } 343 | 344 | .highlight .il { 345 | color: #009999 346 | } 347 | 348 | .highlight .mi { 349 | color: #009999 350 | } 351 | 352 | .highlight .mo { 353 | color: #009999 354 | } 355 | 356 | .highlight .m, .highlight .mb, .highlight .mx { 357 | color: #009999 358 | } 359 | 360 | .highlight .sb { 361 | color: #d14 362 | } 363 | 364 | .highlight .sc { 365 | color: #d14 366 | } 367 | 368 | .highlight .sd { 369 | color: #d14 370 | } 371 | 372 | .highlight .s2 { 373 | color: #d14 374 | } 375 | 376 | .highlight .se { 377 | color: #d14 378 | } 379 | 380 | .highlight .sh { 381 | color: #d14 382 | } 383 | 384 | .highlight .si { 385 | color: #d14 386 | } 387 | 388 | .highlight .sx { 389 | color: #d14 390 | } 391 | 392 | .highlight .sr { 393 | color: #009926 394 | } 395 | 396 | .highlight .s1 { 397 | color: #d14 398 | } 399 | 400 | .highlight .ss { 401 | color: #990073 402 | } 403 | 404 | .highlight .s { 405 | color: #d14 406 | } 407 | 408 | .highlight .na { 409 | color: #008080 410 | } 411 | 412 | .highlight .bp { 413 | color: #999999 414 | } 415 | 416 | .highlight .nb { 417 | color: #0086B3 418 | } 419 | 420 | .highlight .nc { 421 | color: #445588; 422 | font-weight: bold 423 | } 424 | 425 | .highlight .no { 426 | color: #008080 427 | } 428 | 429 | .highlight .nd { 430 | color: #3c5d5d; 431 | font-weight: bold 432 | } 433 | 434 | .highlight .ni { 435 | color: #800080 436 | } 437 | 438 | .highlight .ne { 439 | color: #990000; 440 | font-weight: bold 441 | } 442 | 443 | .highlight .nf { 444 | color: #990000; 445 | font-weight: bold 446 | } 447 | 448 | .highlight .nl { 449 | color: #990000; 450 | font-weight: bold 451 | } 452 | 453 | .highlight .nn { 454 | color: #555555 455 | } 456 | 457 | .highlight .nt { 458 | color: #000080 459 | } 460 | 461 | .highlight .vc { 462 | color: #008080 463 | } 464 | 465 | .highlight .vg { 466 | color: #008080 467 | } 468 | 469 | .highlight .vi { 470 | color: #008080 471 | } 472 | 473 | .highlight .nv { 474 | color: #008080 475 | } 476 | 477 | .highlight .ow { 478 | color: #000000; 479 | font-weight: bold 480 | } 481 | 482 | .highlight .o { 483 | color: #000000; 484 | font-weight: bold 485 | } 486 | 487 | .highlight .w { 488 | color: #bbbbbb 489 | } 490 | 491 | .highlight { 492 | background-color: #f8f8f8 493 | } 494 | 495 | * { 496 | box-sizing: border-box 497 | } 498 | 499 | body { 500 | padding: 0; 501 | margin: 0; 502 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 503 | font-size: 16px; 504 | line-height: 1.5; 505 | color: #606c71 506 | } 507 | 508 | a { 509 | color: #1e6bb8; 510 | text-decoration: none 511 | } 512 | 513 | a:hover { 514 | text-decoration: underline 515 | } 516 | 517 | .btn { 518 | display: inline-block; 519 | margin-bottom: 1rem; 520 | color: rgba(255, 255, 255); 521 | background-color: rgba(255, 255, 255, 0.08); 522 | border-color: rgba(255, 255, 255); 523 | border-style: solid; 524 | border-width: 1px; 525 | border-radius: 0.3rem; 526 | transition: color 0.2s, background-color 0.2s, border-color 0.2s 527 | } 528 | 529 | .btn:hover { 530 | color: rgba(255, 255, 255, 0.8); 531 | text-decoration: none; 532 | background-color: rgba(255, 255, 255, 0.2); 533 | border-color: rgba(255, 255, 255, 0.3) 534 | } 535 | 536 | .btn + .btn { 537 | margin-left: 1rem 538 | } 539 | 540 | @media screen and (min-width: 64em) { 541 | .btn { 542 | padding: 0.75rem 1rem 543 | } 544 | } 545 | 546 | @media screen and (min-width: 42em) and (max-width: 64em) { 547 | .btn { 548 | padding: 0.6rem 0.9rem; 549 | font-size: 0.9rem 550 | } 551 | } 552 | 553 | @media screen and (max-width: 42em) { 554 | .btn { 555 | display: block; 556 | width: 100%; 557 | padding: 0.75rem; 558 | font-size: 0.9rem 559 | } 560 | 561 | .btn + .btn { 562 | margin-top: 1rem; 563 | margin-left: 0 564 | } 565 | } 566 | 567 | .page-header { 568 | color: #fff; 569 | text-align: center; 570 | background-color: #1e73be; 571 | /* background-image: linear-gradient(120deg, #155799, #159957) */ 572 | } 573 | 574 | @media screen and (min-width: 64em) { 575 | .page-header { 576 | padding: 5rem 6rem 577 | } 578 | } 579 | 580 | @media screen and (min-width: 42em) and (max-width: 64em) { 581 | .page-header { 582 | padding: 3rem 4rem 583 | } 584 | } 585 | 586 | @media screen and (max-width: 42em) { 587 | .page-header { 588 | padding: 2rem 1rem 589 | } 590 | } 591 | 592 | .project-name { 593 | margin-top: 0; 594 | margin-bottom: 0.1rem 595 | } 596 | 597 | @media screen and (min-width: 64em) { 598 | .project-name { 599 | font-size: 3.25rem 600 | } 601 | } 602 | 603 | @media screen and (min-width: 42em) and (max-width: 64em) { 604 | .project-name { 605 | font-size: 2.25rem 606 | } 607 | } 608 | 609 | @media screen and (max-width: 42em) { 610 | .project-name { 611 | font-size: 1.75rem 612 | } 613 | } 614 | 615 | .project-tagline { 616 | color: #fff; 617 | margin-bottom: 2rem; 618 | font-weight: bolder; 619 | /* opacity: 0.7 */ 620 | } 621 | 622 | @media screen and (min-width: 64em) { 623 | .project-tagline { 624 | font-size: 1.25rem 625 | } 626 | } 627 | 628 | @media screen and (min-width: 42em) and (max-width: 64em) { 629 | .project-tagline { 630 | font-size: 1.15rem 631 | } 632 | } 633 | 634 | @media screen and (max-width: 42em) { 635 | .project-tagline { 636 | font-size: 1rem 637 | } 638 | } 639 | 640 | .main-content { 641 | word-wrap: break-word 642 | } 643 | 644 | .main-content :first-child { 645 | margin-top: 0 646 | } 647 | 648 | @media screen and (min-width: 64em) { 649 | .main-content { 650 | max-width: 64rem; 651 | padding: 2rem 6rem; 652 | margin: 0 auto; 653 | font-size: 1.1rem 654 | } 655 | } 656 | 657 | @media screen and (min-width: 42em) and (max-width: 64em) { 658 | .main-content { 659 | padding: 2rem 4rem; 660 | font-size: 1.1rem 661 | } 662 | } 663 | 664 | @media screen and (max-width: 42em) { 665 | .main-content { 666 | padding: 2rem 1rem; 667 | font-size: 1rem 668 | } 669 | } 670 | 671 | .main-content img { 672 | max-width: 100% 673 | } 674 | 675 | .main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 { 676 | margin-top: 2rem; 677 | margin-bottom: 1rem; 678 | font-weight: normal; 679 | color: #1e6bb8; 680 | } 681 | 682 | .main-content p { 683 | margin-bottom: 1em 684 | } 685 | 686 | .main-content code { 687 | padding: 2px 4px; 688 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 689 | font-size: 0.9rem; 690 | color: #567482; 691 | background-color: #f3f6fa; 692 | border-radius: 0.3rem 693 | } 694 | 695 | .main-content pre { 696 | padding: 0.8rem; 697 | margin-top: 0; 698 | margin-bottom: 1rem; 699 | font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; 700 | color: #567482; 701 | word-wrap: normal; 702 | background-color: #f3f6fa; 703 | border: solid 1px #dce6f0; 704 | border-radius: 0.3rem 705 | } 706 | 707 | .main-content pre > code { 708 | padding: 0; 709 | margin: 0; 710 | font-size: 0.9rem; 711 | color: #567482; 712 | word-break: normal; 713 | white-space: pre; 714 | background: transparent; 715 | border: 0 716 | } 717 | 718 | .main-content .highlight { 719 | margin-bottom: 1rem 720 | } 721 | 722 | .main-content .highlight pre { 723 | margin-bottom: 0; 724 | word-break: normal 725 | } 726 | 727 | .main-content .highlight pre, .main-content pre { 728 | padding: 0.8rem; 729 | overflow: auto; 730 | font-size: 0.9rem; 731 | line-height: 1.45; 732 | border-radius: 0.3rem; 733 | -webkit-overflow-scrolling: touch 734 | } 735 | 736 | .main-content pre code, .main-content pre tt { 737 | display: inline; 738 | max-width: initial; 739 | padding: 0; 740 | margin: 0; 741 | overflow: initial; 742 | line-height: inherit; 743 | word-wrap: normal; 744 | background-color: transparent; 745 | border: 0 746 | } 747 | 748 | .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { 749 | content: normal 750 | } 751 | 752 | .main-content ul, .main-content ol { 753 | margin-top: 0 754 | } 755 | 756 | .main-content blockquote { 757 | padding: 0 1rem; 758 | margin-left: 0; 759 | color: #819198; 760 | border-left: 0.3rem solid #dce6f0 761 | } 762 | 763 | .main-content blockquote > :first-child { 764 | margin-top: 0 765 | } 766 | 767 | .main-content blockquote > :last-child { 768 | margin-bottom: 0 769 | } 770 | 771 | .main-content table { 772 | display: block; 773 | width: 100%; 774 | overflow: auto; 775 | word-break: normal; 776 | word-break: keep-all; 777 | -webkit-overflow-scrolling: touch 778 | } 779 | 780 | .main-content table th { 781 | font-weight: bold 782 | } 783 | 784 | .main-content table th, .main-content table td { 785 | padding: 0.5rem 1rem; 786 | border: 1px solid #e9ebec 787 | } 788 | 789 | .main-content dl { 790 | padding: 0 791 | } 792 | 793 | .main-content dl dt { 794 | padding: 0; 795 | margin-top: 1rem; 796 | font-size: 1rem; 797 | font-weight: bold 798 | } 799 | 800 | .main-content dl dd { 801 | padding: 0; 802 | margin-bottom: 1rem 803 | } 804 | 805 | .main-content hr { 806 | height: 2px; 807 | padding: 0; 808 | margin: 1rem 0; 809 | background-color: #eff0f1; 810 | border: 0 811 | } 812 | 813 | .site-footer { 814 | padding-top: 2rem; 815 | margin-top: 2rem; 816 | border-top: solid 1px #eff0f1 817 | } 818 | 819 | @media screen and (min-width: 64em) { 820 | .site-footer { 821 | font-size: 1rem 822 | } 823 | } 824 | 825 | @media screen and (min-width: 42em) and (max-width: 64em) { 826 | .site-footer { 827 | font-size: 1rem 828 | } 829 | } 830 | 831 | @media screen and (max-width: 42em) { 832 | .site-footer { 833 | font-size: 0.9rem 834 | } 835 | } 836 | 837 | .site-footer-owner { 838 | display: block; 839 | font-weight: bold 840 | } 841 | 842 | .site-footer-credits { 843 | color: #819198 844 | } 845 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/assets/images/branching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site-legacy/src/assets/images/branching.png -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/assets/images/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site-legacy/src/assets/images/octocat.png -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/assets/images/splat-logo-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site-legacy/src/assets/images/splat-logo-blue.png -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/layouts/default.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 24 | 25 |
26 | <%- body %> 27 |
28 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/layouts/minimal.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include('../partials/head') %> 5 | 6 | 7 | 10 | 11 |
12 | <%- body %> 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/another-page/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Another Page 3 | layout: minimal 4 | --- 5 | ## Welcome to another page 6 | 7 | _Markdown_ page using a different layout. 8 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/ejs-page/index.ejs: -------------------------------------------------------------------------------- 1 | --- 2 | title: EJS Page 3 | --- 4 |

EJS Page!

5 | 6 |

Here are some variables: <%= site.title %> | <%= page.title %>

7 | 8 |

Projects

9 | 10 |

The data below is coming from a JSON file.

11 | 12 | 17 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/hosts.ejs: -------------------------------------------------------------------------------- 1 |

Sub-domains found using crt.sh logs

2 | 3 |

Total Hosts Found - <%= site.hosts.length %>

4 |
5 | <% site.hosts.forEach((host) => { %> 6 | <%= host %> 7 | <% }) %> 8 |
-------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/html-page/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTML Page 3 | --- 4 |

HTML page!

5 | 6 |

This is just HTML, inserted as is into the layout.

7 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/index.ejs: -------------------------------------------------------------------------------- 1 |

Sub-domains found using crt.sh logs

2 | 3 |

Total Hosts Found - <%= site.hosts.length %>

4 |
5 | <% site.hosts.forEach((host) => { %> 6 | <%= host %> 7 | <% }) %> 8 |
-------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/nmap.ejs: -------------------------------------------------------------------------------- 1 |

Nmap Results

2 | <% nmap = JSON.parse(site.nmap) %> 3 | 4 | 6 | 7 |

Hosts

8 | <% hosts = nmap.nmaprun.host || [] %> 9 | <% hosts.forEach((host) => { %> 10 |

<%= host.address.addr %>

11 | 19 |
Ports
20 | 21 | <% ports = host.ports %> 22 | <% ports = ports ? ports.port : [] %> 23 | 28 | <% }) %> 29 | 30 | 32 | 33 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/pages/zap.ejs: -------------------------------------------------------------------------------- 1 |

OWASP ZAP Scan Result

2 | 3 | 4 |
5 | <% Object.keys(site.zapScans).forEach((scanKey) => { %> 6 | <% zap = site.zapScans[scanKey] %> 7 | 8 |

<%= zap.site["@name"] %>

9 | 10 | <% if (! zap.site.alerts) { %> 11 |

No result found

12 | <% return %> 13 | <% } %> 14 | 15 |

Alerts

16 | 17 | 18 |
19 | <% zap.site.alerts.forEach((a) => { %> 20 | [<%= a.pluginid %>] <%= a.name %> 21 | <% }) %> 22 |
23 | 24 | <% }) %> 25 | 26 |
27 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/partials/foot.ejs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /apps/report-generator/site-legacy/src/partials/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= page.title ? `${page.title} | ` : '' %> 4 | <%= site.title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/report-generator/site.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/apps/report-generator/site.tar.gz -------------------------------------------------------------------------------- /apps/report-trigger/README.md: -------------------------------------------------------------------------------- 1 | 2 | kubeless function deploy splat-report-gen -f app.js -d package.json --runtime nodejs8 --handler app.handler 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/report-trigger/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | 5 | Environment variables: 6 | process.env.NATS_URL 7 | 8 | */ 9 | 10 | let NATS = require('nats'); 11 | const NATS_TOPIC = process.env.NATS_TOPIC || 'report-gen-input' 12 | 13 | function publishToNATS(topic, content) { 14 | return new Promise(function (resolve, reject) { 15 | if (typeof(content) === 'object') { 16 | content = JSON.stringify(content) 17 | } 18 | 19 | try { 20 | let nats = NATS.connect(process.env.NATS_URL) 21 | nats.publish(topic, content, function () { 22 | console.log('Submitted to NATS') 23 | nats.close() 24 | resolve() 25 | }) 26 | } catch (err) { 27 | reject(err) 28 | } 29 | }) 30 | } 31 | 32 | 33 | async function handler(event, context) { 34 | let request = event.data 35 | 36 | console.log(`Triggering report for scan_id: ${request.scan_id}`) 37 | 38 | try { 39 | // The message schema matches with what our Go adapter expects 40 | await publishToNATS(NATS_TOPIC, { 41 | ScanID: request.scan_id, 42 | EventType: 'report-gen', 43 | EventValue: request.scan_id 44 | }) 45 | return JSON.stringify({ status: "Success" }) 46 | } 47 | catch (err) { 48 | console.log(`Error occurred: ${err}`) 49 | return JSON.stringify({ status: "Failed" }) 50 | } 51 | } 52 | 53 | module.exports = { 54 | handler 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /apps/report-trigger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "report-fn-trigger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "nats": "^1.2.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/shell-tools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | LABEL MAINTAINER "Appsecco" 3 | 4 | RUN apt-get update && apt-get install wget ruby ruby-dev build-essential curl vim -y \ 5 | && wget https://dl.minio.io/client/mc/release/linux-amd64/mc && chmod +x mc \ 6 | && mv mc /usr/bin/mc \ 7 | && gem install nats 8 | 9 | COPY splat-sidecar /usr/bin/splat-sidecar 10 | -------------------------------------------------------------------------------- /apps/shell-tools/README.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | > Copy adapter binary to this folder as `splat-sidecar` before building docker image 4 | 5 | Build docker image 6 | 7 | ``` 8 | docker build -t REGISTRY/IMAGE . 9 | ``` 10 | 11 | Push docker image 12 | 13 | ``` 14 | gcloud docker -- push REGISTRY/IMAGE 15 | ``` 16 | -------------------------------------------------------------------------------- /apps/zap-workflow/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM owasp/zap2docker-stable 2 | LABEL MAINTAINER "Appsecco" 3 | 4 | RUN mkdir -p /zap/wrk/ 5 | COPY splat-sidecar /usr/bin/splat-sidecar 6 | -------------------------------------------------------------------------------- /apps/zap-workflow/README.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | > Copy adapter binary to this folder as `splat-sidecar` before building docker image 4 | 5 | Build docker image 6 | 7 | ``` 8 | docker build -t REGISTRY/IMAGE . 9 | ``` 10 | 11 | Push docker image 12 | 13 | ``` 14 | gcloud docker -- push REGISTRY/IMAGE 15 | ``` 16 | -------------------------------------------------------------------------------- /gitbook/README.md: -------------------------------------------------------------------------------- 1 | # Containers, Kubernetes and Serverless to automate Appsec and OSINT workflows 2 | 3 | Modern infrastructure like Docker, Kubernetes, Serverless & Cloud Native services are perfect for executing application security workflows and gathering open source intelligence. In this day and age, simply doing ad-hoc security testing is not very effective in thwarting attacks and is inefficient. By leveraging security management workflows for application security and OSINT, we can supercharge small security teams to secure, audit and test large enterprise-wide application infrastructure continuously. 4 | 5 | 6 | ## In this workshop, we will demonstrate how simple it is to 7 | 8 | * Fully Automated Deployment security tools on a Kubernetes (K8S) cluster 9 | * Expose command line tools with REST-based APIs 10 | * Connect everything using serverless and cloud-native services 11 | * All of this safely behind Single Sign-On (SSO) authentication Once everything is set up, we will demonstrate how we can conduct application-level scanning and gathering OSINT with visualizing the data with security dashboards. 12 | 13 | 14 | ## Key features of the workshop 15 | 16 | * We will be using only free and open source tools 17 | * Sharing our knowledge of the setups and tools usage 18 | * Sharing any scripts/configurations that we will use 19 | * 100% live demos with some time to explain our design choices and answer any questions -------------------------------------------------------------------------------- /gitbook/SUMMARY.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | * [Introduction](README.md) 4 | * [Getting Started](getting-started.md) 5 | * [Prerequisites](prerequisites.md) 6 | 7 | ## Cluster Environment Setup 8 | 9 | * [Create Cluster in GKE](cluster-setup/create-cluster-in-gke.md) 10 | * [Install Helm Tiller](cluster-setup/install-helm-tiller.md) 11 | * [Install Nginx Ingress](cluster-setup/install-nginx-ingress.md) 12 | * [Install Cert Manager](cluster-setup/install-cert-manager.md) 13 | * [Install NATS](cluster-setup/install-nats.md) 14 | * [Install Minio](cluster-setup/install-minio.md) 15 | * [Install Kubeless with NATS Trigger](cluster-setup/install-kubeless-with-nats-trigger.md) 16 | 17 | ## App Setup 18 | 19 | * [Deploy Kubeless Functions - UEP, SEP and crtsh scanner](apps-setup/deploy-kubeless-functions.md) 20 | * [Deploy Nmap Workflow](apps-setup/deploy-nmap-workflow.md) 21 | * [Deploy ZAP Workflow](apps-setup/deploy-zap-workflow.md) 22 | * [Deploy HTTP Endpoints Discovery Workflow](apps-setup/deploy-http-endpoints-discovery-workflow.md) 23 | * [Configure Minio Service](apps-setup/configure-minio-service.md) 24 | * [Deploy Reporting Workflow](apps-setup/deploy-reporting-workflow.md) 25 | 26 | ## Execution 27 | 28 | * [Submit Scan Request](execution/submit-scan-request.md) 29 | * [Submit Reporting Request](execution/submit-reporting-request.md) 30 | -------------------------------------------------------------------------------- /gitbook/apps-setup/configure-minio-service.md: -------------------------------------------------------------------------------- 1 | # Configure Minio Service 2 | 3 | * Get into the pod and get the minio server configuration by running the following commands 4 | 5 | ```bash 6 | export SHELL_POD_NAME=$(kubectl get pods --selector app=http-endpoint-detection -o jsonpath="{.items[0].metadata.name}") 7 | 8 | kubectl exec -it $SHELL_POD_NAME bash 9 | 10 | mc config host add minio http://$SPLAT_MINIO_ENDPOINT $SPLAT_MINIO_ACCESS_KEY $SPLAT_MINIO_SECRET_KEY 11 | mc admin config get minio > config.json 12 | ``` 13 | 14 | * Now we have to edit the `config.json` and update the below text respectively. Replace `XXXXXXXXX` with your NATS client password, `AAAAAAAAAAAAA` with minio accesskey and `SSSSSSSSSSSSSSSSSSSSSS` with minio secret key. Also ensure NATS enabled flag is set to `true` 15 | 16 | ```json 17 | ... 18 | "credential": { 19 | "accessKey": "AAAAAAAAAAAAA", 20 | "secretKey": "SSSSSSSSSSSSSSSSSSSSSS", 21 | "expiration": "0001-01-01T00:00:00Z" 22 | }, 23 | ... 24 | 25 | ... 26 | "nats": { 27 | "1": { 28 | "enable": true, 29 | "address": "nats-nats-client.default.svc.cluster.local:4222", 30 | "subject": "minio-bucket-events", 31 | "username": "nats_client", 32 | "password": "XXXXXXXXX", 33 | "token": "", 34 | "secure": false, 35 | "pingInterval": 0, 36 | "streaming": { 37 | "enable": false, 38 | "clusterID": "", 39 | "async": false, 40 | "maxPubAcksInflight": 0 41 | } 42 | } 43 | } 44 | ... 45 | ``` 46 | 47 | * Update the minio server configuration by running the following commands 48 | 49 | ```bash 50 | mc admin config set minio < ./config.json 51 | mc admin service restart minio 52 | 53 | Ctrl + C 54 | 55 | mc mb minio/scandata 56 | mc event add minio/scandata arn:minio:sqs:us-east-1:1:nats 57 | ``` 58 | -------------------------------------------------------------------------------- /gitbook/apps-setup/deploy-http-endpoints-discovery-workflow.md: -------------------------------------------------------------------------------- 1 | # Deploy HTTP Endpoints Discovery workflow 2 | 3 | * The complete HTTP endpoint discovery workflow infrastructure manifest can be deployed into kubernetes cluster by running the following command 4 | 5 | ```bash 6 | kubectl apply -f infra/http-endpoint-workflow/http-endpoint-workflow.yaml 7 | ``` -------------------------------------------------------------------------------- /gitbook/apps-setup/deploy-kubeless-functions.md: -------------------------------------------------------------------------------- 1 | # Deploy Kubeless Funcation 2 | 3 | ## Export required variables for deploying functions 4 | 5 | * Run the following command to export variables required for deployment of fucntions 6 | 7 | ```bash 8 | export MINIO_HOST='minio.default.svc.cluster.local' 9 | export MINIO_PORT='9000' 10 | export MINIO_ACCESS_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.accesskey}' | base64 -d) 11 | export MINIO_SECRET_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.secretkey}' | base64 -d) 12 | export MINIO_BUCKET_NAME='scandata' 13 | export NATS_CLIENT_SECRET=$(kubectl get cm --namespace default nats-nats -o jsonpath='{.data.*}' | grep -m 1 password | awk '{print $2}') 14 | export NATS_URL="nats://nats_client:$NATS_CLIENT_SECRET@nats-nats-client.default.svc.cluster.local:4222" 15 | ``` 16 | 17 | ## Deploy UEP function 18 | 19 | * Deploy kubeless UEP function into kubernetes cluster 20 | 21 | ```bash 22 | cd apps/fn-uep 23 | kubeless function deploy splat-uep -f app.js -d package.json --runtime nodejs8 --handler app.handler --env MINIO_HOST=$MINIO_HOST,MINIO_PORT=$MINIO_PORT,MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY,MINIO_SECRET_KEY=$MINIO_SECRET_KEY,MINIO_BUCKET_NAME=$MINIO_BUCKET_NAME 24 | cd ../../ 25 | ``` 26 | 27 | ## Deploy SEP function 28 | 29 | * Deploy kubeless SEP function into kubernetes cluster 30 | 31 | ```bash 32 | cd apps/fn-sep 33 | kubeless function deploy splat-sep -f app.js -d package.json --runtime nodejs8 --handler app.handler --env MINIO_HOST=$MINIO_HOST,MINIO_PORT=$MINIO_PORT,MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY,MINIO_SECRET_KEY=$MINIO_SECRET_KEY,MINIO_BUCKET_NAME=$MINIO_BUCKET_NAME,NATS_URL=$NATS_URL 34 | cd ../../ 35 | ``` 36 | 37 | * Create NATS trigger for SEP function 38 | 39 | ```bash 40 | kubeless trigger nats create minio-splat-trigger --trigger-topic minio-bucket-events --function-selector created-by=kubeless,function=splat-sep 41 | ``` 42 | 43 | ## Deploy CRT.SH function 44 | 45 | * Deploy kubeless crt.sh function into kubernetes cluster 46 | 47 | ```bash 48 | cd apps/fn-crtsh 49 | kubeless function deploy splat-crtsh-scanner -f app.js -d package.json --runtime nodejs8 --handler app.handler --env MINIO_HOST=$MINIO_HOST,MINIO_PORT=$MINIO_PORT,MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY,MINIO_SECRET_KEY=$MINIO_SECRET_KEY,MINIO_BUCKET_NAME=$MINIO_BUCKET_NAME 50 | cd ../../ 51 | ``` 52 | 53 | * Create NATS trigger for crt.sh function 54 | 55 | ```bash 56 | kubeless trigger nats create crtsh-input-trigger --trigger-topic splat-input-domain --function-selector created-by=kubeless,function=splat-crtsh-scanner 57 | ``` -------------------------------------------------------------------------------- /gitbook/apps-setup/deploy-nmap-workflow.md: -------------------------------------------------------------------------------- 1 | # Deploy NMAP workflow 2 | 3 | * The complete nmap workflow infrastructure manifest can be deployed into kubernetes cluster by running the following command 4 | 5 | ```bash 6 | kubectl apply -f infra/nmap-workflow/nmap-workflow.yaml 7 | ``` -------------------------------------------------------------------------------- /gitbook/apps-setup/deploy-reporting-workflow.md: -------------------------------------------------------------------------------- 1 | # Deploy Reporting Workflow 2 | 3 | * Create a Google Cloud storage service account as a secret in the kubernetes. Ensure the serviceaccount has "Storage Read/Write" access. Also make sure save the file name as `sa.json` 4 | 5 | ```bash 6 | kubectl create secret generic googlesatoken --from-file sa.json 7 | ``` 8 | 9 | * Deploy the reportin workflow into kubernetes 10 | 11 | ```bash 12 | kubectl apply -f infra/report-generator/report-generator.yaml 13 | ``` 14 | 15 | * Deploy the report generator trigger function 16 | 17 | ```bash 18 | cd apps/report-trigger 19 | 20 | export NATS_CLIENT_SECRET=$(kubectl get cm --namespace default nats-nats -o jsonpath='{.data.*}' | grep -m 1 password | awk '{print $2}') 21 | export NATS_URL="nats://nats_client:$NATS_CLIENT_SECRET@nats-nats-client.default.svc.cluster.local:4222" 22 | 23 | kubeless function deploy splat-report-gen -f app.js -d package.json --runtime nodejs8 --handler app.handler --env NATS_URL=$NATS_URL 24 | 25 | cd ../../ 26 | ``` 27 | -------------------------------------------------------------------------------- /gitbook/apps-setup/deploy-zap-workflow.md: -------------------------------------------------------------------------------- 1 | # Deploy OWASP ZAP workflow 2 | 3 | * The complete owasp zap workflow infrastructure manifest can be deployed into kubernetes cluster by running the following command 4 | 5 | ```bash 6 | kubectl apply -f infra/zap-workflow/zap-workflow.yaml 7 | ``` -------------------------------------------------------------------------------- /gitbook/cluster-setup/create-cluster-in-gke.md: -------------------------------------------------------------------------------- 1 | # Create cluster in GKE 2 | 3 | * We will be using Google Kubernetes Engine (GKE) to host the infrastructure 4 | * Make sure you have authenticated to the google cloud using your `gcloud` sdk in your host operating system 5 | 6 | ## Export the variables 7 | 8 | * Export the name of the cluster and project, this we will be using in future for the setup automation 9 | * Make sure replace the `training-automation-stuff` with your google cloud project name 10 | 11 | ```bash 12 | export STUDENTCLUSTERNAME='splat' 13 | export STUDENTPROJECTNAME='training-automation-stuff' 14 | export STUDENTREGION='us-central1' 15 | ``` 16 | 17 | ## Cluster creation 18 | 19 | * Create cluster using the below command 20 | 21 | ```bash 22 | gcloud beta container --project "$STUDENTPROJECTNAME" clusters create "$STUDENTCLUSTERNAME" --zone "$STUDENTREGION-a" --no-enable-basic-auth --cluster-version "1.11.6-gke.2" --machine-type "n1-standard-1" --image-type "COS" --disk-type "pd-standard" --disk-size "50" --metadata disable-legacy-endpoints=true --scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" --preemptible --num-nodes "3" --enable-cloud-logging --enable-cloud-monitoring --no-enable-ip-alias --network "projects/$STUDENTPROJECTNAME/global/networks/default" --subnetwork "projects/$STUDENTPROJECTNAME/regions/$STUDENTREGION/subnetworks/default" --enable-autoscaling --min-nodes "2" --max-nodes "5" --addons HorizontalPodAutoscaling,HttpLoadBalancing --enable-autoupgrade --enable-autorepair --maintenance-window "21:30" 23 | ``` 24 | 25 | ## Static IP for nginx-ingress 26 | 27 | * Create the static IP for the cluster 28 | 29 | ```bash 30 | gcloud compute addresses create $STUDENTCLUSTERNAME-sip --region $STUDENTREGION --project $STUDENTPROJECTNAME 31 | export STUDENTCLUSTERSIP=$(gcloud compute addresses list --filter "name=$STUDENTCLUSTERNAME-sip" | grep $STUDENTCLUSTERNAME-sip | awk '{print $2}') 32 | ``` 33 | 34 | 35 | ## Generating GKE cluster credentials for accessing 36 | 37 | * Generate cluster credentials 38 | 39 | ```bash 40 | gcloud container clusters get-credentials $STUDENTCLUSTERNAME --zone $STUDENTREGION-a --project $STUDENTPROJECTNAME 41 | ``` 42 | -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-cert-manager.md: -------------------------------------------------------------------------------- 1 | # Install cert-manager 2 | 3 | `cert-manager` is a Kubernetes add-on to automate the management and issuance of TLS certificates from various issuing sources. 4 | 5 | 6 | * Deploy cert-manager into the kubernetes cluster 7 | 8 | ```bash 9 | helm install --name cert-manager --namespace kube-system stable/cert-manager 10 | ``` 11 | 12 | ## Deploy ClusterIssuer for issuing certificates 13 | 14 | * Now we will deploy the `ClusterIssuer` for issuing TLS (lets encrypt) certificates for applications inside the cluster 15 | 16 | ```bash 17 | kubectl apply -f infra/cert-manager/clusterissuer.yaml 18 | ``` 19 | -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-helm-tiller.md: -------------------------------------------------------------------------------- 1 | # Installing Helm and Tiller 2 | 3 | `Helm` is the package manager for Kubernetes 4 | 5 | * Deploy tiller service into the cluster using helm 6 | 7 | ```bash 8 | kubectl apply -f infra/helm-rbac/helm-rbac.yaml 9 | helm init --service-account tiller 10 | ``` 11 | 12 | * Fix the helm tiller default vulnerability, which exposes the tiller service to every pod inside the cluster 13 | 14 | ```bash 15 | kubectl -n kube-system delete service tiller-deploy 16 | kubectl -n kube-system patch deployment tiller-deploy --patch ' 17 | spec: 18 | template: 19 | spec: 20 | containers: 21 | - name: tiller 22 | ports: [] 23 | command: ["/tiller"] 24 | args: ["--listen=localhost:44134"] 25 | ' 26 | sleep 15 27 | ``` 28 | 29 | * Ensure helm is communicating the tiller service 30 | 31 | ```bash 32 | helm version 33 | ``` -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-kubeless-with-nats-trigger.md: -------------------------------------------------------------------------------- 1 | # Install Kubeless with NATS Trigger 2 | 3 | ## Exporting the common secrets required 4 | 5 | * Export common variables for creating a kuebrnetes secret 6 | 7 | ```bash 8 | export MINIO_HOST='minio.default.svc.cluster.local' 9 | export MINIO_PORT='9000' 10 | export MINIO_ACCESS_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.accesskey}' | base64 -d) 11 | export MINIO_SECRET_KEY=$(kubectl get secret my-minio-secret -o jsonpath='{.data.secretkey}' | base64 -d) 12 | export MINIO_BUCKET_NAME='scandata' 13 | export NATS_CLIENT_SECRET=$(kubectl get cm --namespace default nats-nats -o jsonpath='{.data.*}' | grep -m 1 password | awk '{print $2}') 14 | export NATS_URL="nats://nats_client:$NATS_CLIENT_SECRET@nats-nats-client.default.svc.cluster.local:4222" 15 | ``` 16 | 17 | * Create kuberenetes secrets 18 | 19 | ```bash 20 | kubectl create secret generic common-env-secrets --from-literal=SPLAT_MINIO_ENDPOINT=$MINIO_HOST:$MINIO_PORT --from-literal=SPLAT_MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY --from-literal=SPLAT_MINIO_SECRET_KEY=$MINIO_SECRET_KEY --from-literal=MINIO_OUTPUT_BUCKET=$MINIO_BUCKET_NAME --from-literal=SPLAT_NATS_URL=$NATS_URL 21 | 22 | kubectl create ns kubeless 23 | 24 | kubectl -n kubeless create secret generic common-env-secrets --from-literal=SPLAT_NATS_URL=$NATS_URL 25 | ``` 26 | 27 | ## Deploy Kubeless 28 | 29 | * Add the user to clusterrolebinding. Replace the `user@domain.com` with your Gmail used to access this cluster 30 | 31 | ```bash 32 | kubectl create clusterrolebinding kubeless-cluster-admin --clusterrole=cluster-admin --user=user@domain.com 33 | ``` 34 | 35 | * Deploying the kubeless and nats trigger 36 | 37 | ```bash 38 | kubectl apply -f kubeless/kubeless-v1.0.2.yaml 39 | kubectl apply -f kubeless/nats-v1.0.0-alpha.9.yaml 40 | ``` 41 | -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-minio.md: -------------------------------------------------------------------------------- 1 | # Install Minio 2 | 3 | * Deploy the Minio service into kubernetes cluster. Make sure replace the `accesskey` and `secretkey` values with random generated secrets 4 | 5 | ```bash 6 | kubectl create secret generic my-minio-secret --from-literal=accesskey=foobarbaz --from-literal=secretkey=foobarbazqux 7 | 8 | helm install --name minio --set existingSecret=my-minio-secret stable/minio 9 | ``` 10 | -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-nats.md: -------------------------------------------------------------------------------- 1 | # Install NATS 2 | 3 | * Deploy the NATS service in kubernetes cluster 4 | 5 | ```bash 6 | helm install --name nats stable/nats 7 | ``` 8 | 9 | * Export the NATS user and password for later use 10 | 11 | ```bash 12 | export NATS_USER=$(kubectl get cm --namespace default nats-nats -o jsonpath='{.data.*}' | grep -m 1 user | awk '{print $2}') 13 | export NATS_PASS=$(kubectl get cm --namespace default nats-nats -o jsonpath='{.data.*}' | grep -m 1 password | awk '{print $2}') 14 | ``` 15 | -------------------------------------------------------------------------------- /gitbook/cluster-setup/install-nginx-ingress.md: -------------------------------------------------------------------------------- 1 | # Installing Nginx-Ingress 2 | 3 | * Deploy nginx-ingress helm chart into the cluster 4 | 5 | ```bash 6 | helm install --namespace kube-system --name nginx-ingress stable/nginx-ingress --set rbac.create=true,controller.service.loadBalancerIP=$STUDENTCLUSTERSIP 7 | ``` 8 | 9 | ## Deploy the ingress resource for apps 10 | 11 | * Update the `infra/apps-ingress-tls/nginx-ingress-tls.yaml` file. Replace `subdomain.domain.com` with your domain to generate TLS and expose ingress resource 12 | 13 | * Make sure your DNS is pointing to nginx ingress static IP 14 | 15 | ```bash 16 | echo $STUDENTCLUSTERSIP 17 | 18 | dig subdomain.domain.com A +short | grep $STUDENTCLUSTERSIP 19 | ``` 20 | 21 | * Deploy the nginx-ingress with TLS certificate generation 22 | 23 | ```yaml 24 | kubectl apply -f infra/apps-ingress-tls/nginx-ingress-tls.yaml 25 | ``` 26 | -------------------------------------------------------------------------------- /gitbook/execution/submit-reporting-request.md: -------------------------------------------------------------------------------- 1 | # Report Generation 2 | 3 | A `scan_id` is used to trigger report generator for the specific domain scan. 4 | 5 | ``` 6 | curl -XPOST -H 'Content-Type: application/json' -d '{ "scan_id": "" }' https://cluster-name/report 7 | ``` 8 | 9 | > The current system constraint prevent knowing the status i.e. when and if all scanners have finished. To generate a report, trigger this report generation request after reasonable time or based on container output logs. -------------------------------------------------------------------------------- /gitbook/execution/submit-scan-request.md: -------------------------------------------------------------------------------- 1 | # Scan Submission 2 | 3 | A `domain` can be submitted for scanning using the `cURL` command below: 4 | 5 | ``` 6 | curl -XPOST -H 'Content-Type: application/json' -d '{ "target_domain": "example.com" }' https://cluster-name/scan 7 | ``` 8 | 9 | This will return a `scan_id` as part of response JSON. This `scan_id` is required for subsequent API call to generate report. -------------------------------------------------------------------------------- /gitbook/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | We have documented and pushed all the code, scripts and files to Github repository. You can follow the below instructions to get started with the cluster setup, deployment. 4 | 5 | 6 | ## Cloning the Github Repository 7 | 8 | * Clone the github repository to download the source code required to setup 9 | 10 | ```bash 11 | git clone https://github.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows.git 12 | cd using-docker-kubernetes-for-automating-appsec-and-osint-workflows 13 | ``` 14 | 15 | * Now we will follow the gitbook instructions to seutp the entire cluster and end to end workflows -------------------------------------------------------------------------------- /gitbook/prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | We have some of the prerequisite to completely then entire workflow smoothly. We have referenced the links to download required software and installation instructions 4 | 5 | ## Google Cloud SDK 6 | 7 | * As we are primarily using Google Cloud Platform for all of our setup and installations, we need to have access to Google Cloud SDK 8 | * Install Google Cloud SDK from [https://cloud.google.com/sdk/install](https://cloud.google.com/sdk/install) 9 | * Authenticate to Google Cloud SDK using [https://cloud.google.com/sdk/docs/initializing](https://cloud.google.com/sdk/docs/initializing) 10 | 11 | 12 | ## Kubectl 13 | 14 | * We will be using `kubectl` to communicate with our kubernetes cluster for deploying, managing cluster operations 15 | * Install `kubectl` binary from [https://kubernetes.io/docs/tasks/tools/install-kubectl/](https://kubernetes.io/docs/tasks/tools/install-kubectl/) 16 | 17 | 18 | ## Helm 19 | 20 | * We will be using `helm` to deploy and manage charts into kubernetes cluster 21 | * Install `helm` binary from [https://github.com/helm/helm#install](https://github.com/helm/helm#install) 22 | 23 | ## Kubeless 24 | 25 | * We will be using `kubeless` to deploy our serverless functions and triggers 26 | * Install `kubeless` binary from [https://kubeless.io/docs/quick-start/](https://kubeless.io/docs/quick-start/) -------------------------------------------------------------------------------- /images/splat-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/images/splat-logo.png -------------------------------------------------------------------------------- /infra/apps-ingress-tls/nginx-ingress-tls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: nginx-app-ingress 5 | annotations: 6 | ingress.kubernetes.io/ssl-redirect: "true" 7 | kubernetes.io/tls-acme: "true" 8 | certmanager.k8s.io/cluster-issuer: letsencrypt-prod 9 | kubernetes.io/ingress.class: "nginx" 10 | nginx.ingress.kubernetes.io/rewrite-target: / 11 | 12 | spec: 13 | tls: 14 | - hosts: 15 | - subdomain.domain.com 16 | secretName: nginx-app-prod-letsencrypt 17 | rules: 18 | - host: subdomain.domain.com 19 | http: 20 | paths: 21 | - path: /scan 22 | backend: 23 | serviceName: splat-uep 24 | servicePort: 8080 25 | - path: /report 26 | backend: 27 | serviceName: splat-report-gen 28 | servicePort: 8080 29 | -------------------------------------------------------------------------------- /infra/cert-manager/clusterissuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: certmanager.k8s.io/v1alpha1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt-prod 5 | spec: 6 | acme: 7 | server: https://acme-v02.api.letsencrypt.org/directory 8 | email: "user@domain.com" 9 | privateKeySecretRef: 10 | name: letsencrypt-prod 11 | http01: {} 12 | -------------------------------------------------------------------------------- /infra/helm-rbac/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/infra/helm-rbac/README.md -------------------------------------------------------------------------------- /infra/helm-rbac/helm-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tiller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: tiller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: tiller 18 | namespace: kube-system 19 | 20 | -------------------------------------------------------------------------------- /infra/http-endpoint-workflow/http-endpoint-workflow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: http-endpoint-detection 5 | labels: 6 | app: http-endpoint-detection 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: http-endpoint-detection 12 | template: 13 | metadata: 14 | labels: 15 | app: http-endpoint-detection 16 | spec: 17 | containers: 18 | - name: curl-ctr 19 | imagePullPolicy: Always 20 | image: appsecco/splat-workshop-shell-tools 21 | command: ["/usr/bin/splat-sidecar"] 22 | env: 23 | - name: SPLAT_MINIO_ENDPOINT 24 | valueFrom: 25 | secretKeyRef: 26 | name: common-env-secrets 27 | key: SPLAT_MINIO_ENDPOINT 28 | - name: SPLAT_MINIO_ACCESS_KEY 29 | valueFrom: 30 | secretKeyRef: 31 | name: common-env-secrets 32 | key: SPLAT_MINIO_ACCESS_KEY 33 | - name: SPLAT_MINIO_SECRET_KEY 34 | valueFrom: 35 | secretKeyRef: 36 | name: common-env-secrets 37 | key: SPLAT_MINIO_SECRET_KEY 38 | - name: MINIO_OUTPUT_BUCKET 39 | valueFrom: 40 | secretKeyRef: 41 | name: common-env-secrets 42 | key: MINIO_OUTPUT_BUCKET 43 | - name: SPLAT_NATS_URL 44 | valueFrom: 45 | secretKeyRef: 46 | name: common-env-secrets 47 | key: SPLAT_NATS_URL 48 | - name: SPLAT_MINIO_FILE_PATTERN 49 | value: scans/{{SCAN_ID}}/{{OUTPUT_EVENT}}/data.txt 50 | - name: SPLAT_MINIO_EVENT_NAME 51 | value: http-endpoints 52 | - name: SPLAT_NATS_CONSUMER_TOPIC 53 | value: nmap-input 54 | - name: SPLAT_EXEC_TIMEOUT 55 | value: "500" 56 | - name: SPLAT_MINIO_CAPTURE_STDOUT 57 | value: "yes" 58 | - name: SPLAT_EXEC_PATTERN 59 | value: "CURL_OPTIONS=\"--connect-timeout 3 --max-time 10\"; TARGETS=\"{{TARGET}}\"; for x in $TARGETS; do curl $CURL_OPTIONS -I http://$x > /dev/null 2>&1 && echo http://$x; curl $CURL_OPTIONS -I https://$x > /dev/null 2>&1 && echo https://$x; done; exit 0;" 60 | -------------------------------------------------------------------------------- /infra/kubeless/kubeless-v1.0.2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | data: 4 | builder-image: kubeless/function-image-builder:v1.0.2 5 | builder-image-secret: "" 6 | deployment: '{}' 7 | enable-build-step: "false" 8 | function-registry-tls-verify: "true" 9 | ingress-enabled: "false" 10 | provision-image: kubeless/unzip@sha256:f162c062973cca05459834de6ed14c039d45df8cdb76097f50b028a1621b3697 11 | provision-image-secret: "" 12 | runtime-images: '[{"ID": "ballerina", "depName": "", "fileNameSuffix": ".bal", "versions": 13 | [{"images": [{"command": "/compile-function.sh $KUBELESS_FUNC_NAME", "image": 14 | "ballerina/kubeless-ballerina-init@sha256:a04ca9d289c62397d0b493876f6a9ff4cc425563a47aa7e037c3b850b8ceb3e8", 15 | "phase": "compilation"}, {"image": "ballerina/kubeless-ballerina@sha256:a025841010cfdf8136396efef31d4155283770d331ded6a9003e6e55f02db2e5", 16 | "phase": "runtime"}], "name": "ballerina0.981.0", "version": "0.981.0"}]}, {"ID": 17 | "dotnetcore", "depName": "project.csproj", "fileNameSuffix": ".cs", "versions": 18 | [{"images": [{"command": "/app/compile-function.sh $KUBELESS_INSTALL_VOLUME", 19 | "image": "allantargino/aspnetcore-build@sha256:0d60f845ff6c9c019362a68b87b3920f3eb2d32f847f2d75e4d190cc0ce1d81c", 20 | "phase": "compilation"}, {"env": {"DOTNETCORE_HOME": "$(KUBELESS_INSTALL_VOLUME)/packages"}, 21 | "image": "allantargino/kubeless-dotnetcore@sha256:1699b07d9fc0276ddfecc2f823f272d96fd58bbab82d7e67f2fd4982a95aeadc", 22 | "phase": "runtime"}], "name": "dotnetcore2.0", "version": "2.0"}, {"images": [{"command": 23 | "/app/compile-function.sh $KUBELESS_INSTALL_VOLUME", "image": "allantargino/aspnetcore-build@sha256:36123cf0279b87c5d27d69558062678a5353cc6db238af46bd5c0e508109f659", 24 | "phase": "compilation"}, {"env": {"DOTNETCORE_HOME": "$(KUBELESS_INSTALL_VOLUME)/packages"}, 25 | "image": "allantargino/kubeless-dotnetcore@sha256:6d6c659807881e9dac7adde305867163ced5711ef77a3a76e50112bca1ba14cf", 26 | "phase": "runtime"}], "name": "dotnetcore2.1", "version": "2.1"}]}, {"ID": "go", 27 | "depName": "Gopkg.toml", "fileNameSuffix": ".go", "versions": [{"images": [{"command": 28 | "/compile-function.sh", "image": "kubeless/go-init@sha256:88104a60bcd4c67fd6aa92fffa46062396c08bc2632529ee435517e7628a2f95", 29 | "phase": "compilation"}, {"command": "cd $GOPATH/src/kubeless && dep ensure > 30 | /dev/termination-log 2>&1", "image": "kubeless/go-init@sha256:88104a60bcd4c67fd6aa92fffa46062396c08bc2632529ee435517e7628a2f95", 31 | "phase": "installation"}, {"image": "kubeless/go@sha256:f5d449f830ac8727c0b9c05e458b6dd0a0822743cb19a87343e0fd00b041eea9", 32 | "phase": "runtime"}], "name": "go1.10", "version": "1.10"}]}, {"ID": "java", "depName": 33 | "pom.xml", "fileNameSuffix": ".java", "versions": [{"images": [{"command": "/compile-function.sh", 34 | "image": "kubeless/java-init@sha256:36cc37836437aaa5ac82f49ba20781d45bd5efcd9e2d022fcaae487a058572c2", 35 | "phase": "compilation"}, {"image": "kubeless/java@sha256:2dbc44c0e2467a27558776edb2aed85055361e2f0b74443800851ee658576a88", 36 | "phase": "runtime"}], "name": "java1.8", "version": "1.8"}]}, {"ID": "nodejs", 37 | "depName": "package.json", "fileNameSuffix": ".js", "versions": [{"images": [{"command": 38 | "/kubeless-npm-install.sh", "image": "kubeless/nodejs@sha256:10f6a3d3e0782220d3a20a0847130846d554e1f196898d928ed986d8a21ff00f", 39 | "phase": "installation"}, {"env": {"NODE_PATH": "$(KUBELESS_INSTALL_VOLUME)/node_modules"}, 40 | "image": "kubeless/nodejs@sha256:10f6a3d3e0782220d3a20a0847130846d554e1f196898d928ed986d8a21ff00f", 41 | "phase": "runtime"}], "name": "node6", "version": "6"}, {"images": [{"command": 42 | "/kubeless-npm-install.sh", "image": "kubeless/nodejs@sha256:5f1e999a1021dfb3d117106d80519a82110bd26a579f067f1ff7127025c90be5", 43 | "phase": "installation"}, {"env": {"NODE_PATH": "$(KUBELESS_INSTALL_VOLUME)/node_modules"}, 44 | "image": "kubeless/nodejs@sha256:5f1e999a1021dfb3d117106d80519a82110bd26a579f067f1ff7127025c90be5", 45 | "phase": "runtime"}], "name": "node8", "version": "8"}]}, {"ID": "php", "depName": 46 | "composer.json", "fileNameSuffix": ".php", "versions": [{"images": [{"command": 47 | "composer install -d $KUBELESS_INSTALL_VOLUME", "image": "composer:1.6", "phase": 48 | "installation"}, {"image": "kubeless/php@sha256:9b86066b2640bedcd88acb27f43dfaa2b338f0d74d9d91131ea781402f7ec8ec", 49 | "phase": "runtime"}], "name": "php72", "version": "7.2"}]}, {"ID": "python", "depName": 50 | "requirements.txt", "fileNameSuffix": ".py", "versions": [{"images": [{"command": 51 | "pip install --prefix=$KUBELESS_INSTALL_VOLUME -r $KUBELESS_DEPS_FILE", "image": 52 | "python:2.7", "phase": "installation"}, {"env": {"PYTHONPATH": "$(KUBELESS_INSTALL_VOLUME)/lib/python2.7/site-packages:$(KUBELESS_INSTALL_VOLUME)"}, 53 | "image": "kubeless/python@sha256:34332f4530508a810f491838a924c36ceac0ec7cab487520e2db2b037800ecda", 54 | "phase": "runtime"}], "name": "python27", "version": "2.7"}, {"images": [{"command": 55 | "pip install --prefix=$KUBELESS_INSTALL_VOLUME -r $KUBELESS_DEPS_FILE", "image": 56 | "python:3.4", "phase": "installation"}, {"env": {"PYTHONPATH": "$(KUBELESS_INSTALL_VOLUME)/lib/python3.4/site-packages:$(KUBELESS_INSTALL_VOLUME)"}, 57 | "image": "kubeless/python@sha256:5c93a60b83dba9324ad8358e66952232746ef9d477266d6a199617d7344c2053", 58 | "phase": "runtime"}], "name": "python34", "version": "3.4"}, {"images": [{"command": 59 | "pip install --prefix=$KUBELESS_INSTALL_VOLUME -r $KUBELESS_DEPS_FILE", "image": 60 | "python:3.6", "phase": "installation"}, {"env": {"PYTHONPATH": "$(KUBELESS_INSTALL_VOLUME)/lib/python3.6/site-packages:$(KUBELESS_INSTALL_VOLUME)"}, 61 | "image": "kubeless/python@sha256:8c49bfa1c6aa5fbcd0f7d99d97280c161247fc94c06d26c04e39ac341c3f75e5", 62 | "phase": "runtime"}], "name": "python36", "version": "3.6"}, {"images": [{"command": 63 | "pip install --prefix=$KUBELESS_INSTALL_VOLUME -r $KUBELESS_DEPS_FILE", "image": 64 | "python:3.7", "phase": "installation"}, {"env": {"PYTHONPATH": "$(KUBELESS_INSTALL_VOLUME)/lib/python3.7/site-packages:$(KUBELESS_INSTALL_VOLUME)"}, 65 | "image": "kubeless/python@sha256:dbf616cb06a262482c00f5b53e1de17571924032e0ad000865ec6b5357ff35bf", 66 | "phase": "runtime"}], "name": "python37", "version": "3.7"}]}, {"ID": "ruby", 67 | "depName": "Gemfile", "fileNameSuffix": ".rb", "versions": [{"images": [{"command": 68 | "bundle install --gemfile=$KUBELESS_DEPS_FILE --path=$KUBELESS_INSTALL_VOLUME", 69 | "image": "bitnami/ruby:2.3", "phase": "installation"}, {"env": {"GEM_HOME": "$(KUBELESS_INSTALL_VOLUME)/ruby/2.3.0"}, 70 | "image": "kubeless/ruby@sha256:67870b57adebc4dc749a8a19795da801da2d05fc6e8324168ac1b227bb7c77f7", 71 | "phase": "runtime"}], "name": "ruby23", "version": "2.3"}, {"images": [{"command": 72 | "bundle install --gemfile=$KUBELESS_DEPS_FILE --path=$KUBELESS_INSTALL_VOLUME", 73 | "image": "bitnami/ruby:2.4", "phase": "installation"}, {"env": {"GEM_HOME": "$(KUBELESS_INSTALL_VOLUME)/ruby/2.4.0"}, 74 | "image": "kubeless/ruby@sha256:aaa9c7f3dfd4f866a527c04171c32dae2efa420d770a6af9c517771137ab4011", 75 | "phase": "runtime"}], "name": "ruby24", "version": "2.4"}, {"images": [{"command": 76 | "bundle install --gemfile=$KUBELESS_DEPS_FILE --path=$KUBELESS_INSTALL_VOLUME", 77 | "image": "bitnami/ruby:2.5", "phase": "installation"}, {"env": {"GEM_HOME": "$(KUBELESS_INSTALL_VOLUME)/ruby/2.5.0"}, 78 | "image": "kubeless/ruby@sha256:577e35724996ba340ff0a18366bce99586b0be58e4d27fa3e8038f977caf1559", 79 | "phase": "runtime"}], "name": "ruby25", "version": "2.5"}]}, {"ID": "jvm", "depName": 80 | "", "fileNameSuffix": ".jar", "versions": [{"images": [{"command": "mv /kubeless/* 81 | /kubeless/payload.jar && cp /opt/*.jar /kubeless/ > /dev/termination-log 2>&1", 82 | "image": "caraboides/jvm-init@sha256:e57dbf3f56570a196d68bce1c0695102b2dbe3ae2ca6d1c704476a7a11542f1d", 83 | "phase": "compilation"}, {"image": "caraboides/jvm@sha256:2870c4f48df4feb2ee7478a152b44840d781d4b1380ad3fa44b3c7ff314faded", 84 | "phase": "runtime"}], "name": "jvm1.8", "version": "1.8"}]}, {"ID": "nodejs_distroless", 85 | "depName": "package.json", "fileNameSuffix": ".js", "versions": [{"images": [{"command": 86 | "/kubeless-npm-install.sh", "image": "kubeless/nodejs@sha256:424add88dc2a7fdc45012593159794d59a6ea4aafadfffb632d21ae53b1d262b", 87 | "phase": "installation"}, {"env": {"NODE_PATH": "$(KUBELESS_INSTALL_VOLUME)/node_modules"}, 88 | "image": "kubeless/nodejs-distroless@sha256:1fa0469c5520f4e08d89b1fafd2cacf03f098b96ea04997fa52bb9ef2a180fb3", 89 | "phase": "runtime"}], "name": "node8", "version": "8"}]}, {"ID": "nodejsCE", "depName": 90 | "package.json", "fileNameSuffix": ".js", "versions": [{"images": [{"command": 91 | "/kubeless-npm-install.sh", "image": "kubeless/nodejs@sha256:456d98f6f15588b21f5110facf1cc203065840d4c227afa61d17c6c1fa98b3b6", 92 | "phase": "installation"}, {"env": {"NODE_PATH": "$(KUBELESS_INSTALL_VOLUME)/node_modules"}, 93 | "image": "andresmgot/nodejs-ce@sha256:708c265d22a8a1599e05da844d26bc63e2f66f859ffecd2fcb541ecac9c66780", 94 | "phase": "runtime"}], "name": "node8", "version": "8"}]}, {"ID": "vertx", "depName": 95 | "pom.xml", "fileNameSuffix": ".java", "versions": [{"images": [{"command": "/compile-function.sh", 96 | "image": "oscardovao/vertx-init@sha256:6665629b3239eb1d81654381b02c3dd4b87ddb0a1b0b49acc165f0ff53264e0b", 97 | "phase": "compilation"}, {"image": "oscardovao/vertx@sha256:96243e5937a875422d6165e59f1fdb350f1a6d5befbd89f26968abea4345ade1", 98 | "phase": "runtime"}], "name": "vertx1.8", "version": "1.8"}]}]' 99 | service-type: ClusterIP 100 | kind: ConfigMap 101 | metadata: 102 | name: kubeless-config 103 | namespace: kubeless 104 | --- 105 | apiVersion: apps/v1beta1 106 | kind: Deployment 107 | metadata: 108 | labels: 109 | kubeless: controller 110 | name: kubeless-controller-manager 111 | namespace: kubeless 112 | spec: 113 | selector: 114 | matchLabels: 115 | kubeless: controller 116 | template: 117 | metadata: 118 | labels: 119 | kubeless: controller 120 | spec: 121 | containers: 122 | - env: 123 | - name: KUBELESS_INGRESS_ENABLED 124 | valueFrom: 125 | configMapKeyRef: 126 | key: ingress-enabled 127 | name: kubeless-config 128 | - name: KUBELESS_SERVICE_TYPE 129 | valueFrom: 130 | configMapKeyRef: 131 | key: service-type 132 | name: kubeless-config 133 | - name: KUBELESS_NAMESPACE 134 | valueFrom: 135 | fieldRef: 136 | fieldPath: metadata.namespace 137 | - name: KUBELESS_CONFIG 138 | value: kubeless-config 139 | image: kubeless/function-controller:v1.0.2 140 | imagePullPolicy: IfNotPresent 141 | name: kubeless-function-controller 142 | - env: 143 | - name: KUBELESS_INGRESS_ENABLED 144 | valueFrom: 145 | configMapKeyRef: 146 | key: ingress-enabled 147 | name: kubeless-config 148 | - name: KUBELESS_SERVICE_TYPE 149 | valueFrom: 150 | configMapKeyRef: 151 | key: service-type 152 | name: kubeless-config 153 | - name: KUBELESS_NAMESPACE 154 | valueFrom: 155 | fieldRef: 156 | fieldPath: metadata.namespace 157 | - name: KUBELESS_CONFIG 158 | value: kubeless-config 159 | image: bitnami/http-trigger-controller:v1.0.0-alpha.9 160 | imagePullPolicy: IfNotPresent 161 | name: http-trigger-controller 162 | - env: 163 | - name: KUBELESS_INGRESS_ENABLED 164 | valueFrom: 165 | configMapKeyRef: 166 | key: ingress-enabled 167 | name: kubeless-config 168 | - name: KUBELESS_SERVICE_TYPE 169 | valueFrom: 170 | configMapKeyRef: 171 | key: service-type 172 | name: kubeless-config 173 | - name: KUBELESS_NAMESPACE 174 | valueFrom: 175 | fieldRef: 176 | fieldPath: metadata.namespace 177 | - name: KUBELESS_CONFIG 178 | value: kubeless-config 179 | image: bitnami/cronjob-trigger-controller:v1.0.0 180 | imagePullPolicy: IfNotPresent 181 | name: cronjob-trigger-controller 182 | serviceAccountName: controller-acct 183 | --- 184 | apiVersion: v1 185 | kind: ServiceAccount 186 | metadata: 187 | name: controller-acct 188 | namespace: kubeless 189 | --- 190 | apiVersion: rbac.authorization.k8s.io/v1beta1 191 | kind: ClusterRole 192 | metadata: 193 | name: kubeless-controller-deployer 194 | rules: 195 | - apiGroups: 196 | - "" 197 | resources: 198 | - services 199 | - configmaps 200 | verbs: 201 | - create 202 | - get 203 | - delete 204 | - list 205 | - update 206 | - patch 207 | - apiGroups: 208 | - apps 209 | - extensions 210 | resources: 211 | - deployments 212 | verbs: 213 | - create 214 | - get 215 | - delete 216 | - list 217 | - update 218 | - patch 219 | - apiGroups: 220 | - "" 221 | resources: 222 | - pods 223 | verbs: 224 | - list 225 | - delete 226 | - apiGroups: 227 | - "" 228 | resourceNames: 229 | - kubeless-registry-credentials 230 | resources: 231 | - secrets 232 | verbs: 233 | - get 234 | - apiGroups: 235 | - kubeless.io 236 | resources: 237 | - functions 238 | - httptriggers 239 | - cronjobtriggers 240 | verbs: 241 | - get 242 | - list 243 | - watch 244 | - update 245 | - delete 246 | - apiGroups: 247 | - batch 248 | resources: 249 | - cronjobs 250 | - jobs 251 | verbs: 252 | - create 253 | - get 254 | - delete 255 | - deletecollection 256 | - list 257 | - update 258 | - patch 259 | - apiGroups: 260 | - autoscaling 261 | resources: 262 | - horizontalpodautoscalers 263 | verbs: 264 | - create 265 | - get 266 | - delete 267 | - list 268 | - update 269 | - patch 270 | - apiGroups: 271 | - apiextensions.k8s.io 272 | resources: 273 | - customresourcedefinitions 274 | verbs: 275 | - get 276 | - list 277 | - apiGroups: 278 | - monitoring.coreos.com 279 | resources: 280 | - alertmanagers 281 | - prometheuses 282 | - servicemonitors 283 | verbs: 284 | - '*' 285 | - apiGroups: 286 | - extensions 287 | resources: 288 | - ingresses 289 | verbs: 290 | - create 291 | - get 292 | - list 293 | - update 294 | - delete 295 | --- 296 | apiVersion: rbac.authorization.k8s.io/v1beta1 297 | kind: ClusterRoleBinding 298 | metadata: 299 | name: kubeless-controller-deployer 300 | roleRef: 301 | apiGroup: rbac.authorization.k8s.io 302 | kind: ClusterRole 303 | name: kubeless-controller-deployer 304 | subjects: 305 | - kind: ServiceAccount 306 | name: controller-acct 307 | namespace: kubeless 308 | --- 309 | apiVersion: apiextensions.k8s.io/v1beta1 310 | kind: CustomResourceDefinition 311 | metadata: 312 | name: functions.kubeless.io 313 | spec: 314 | group: kubeless.io 315 | names: 316 | kind: Function 317 | plural: functions 318 | singular: function 319 | scope: Namespaced 320 | version: v1beta1 321 | --- 322 | apiVersion: apiextensions.k8s.io/v1beta1 323 | kind: CustomResourceDefinition 324 | metadata: 325 | name: httptriggers.kubeless.io 326 | spec: 327 | group: kubeless.io 328 | names: 329 | kind: HTTPTrigger 330 | plural: httptriggers 331 | singular: httptrigger 332 | scope: Namespaced 333 | version: v1beta1 334 | --- 335 | apiVersion: apiextensions.k8s.io/v1beta1 336 | kind: CustomResourceDefinition 337 | metadata: 338 | name: cronjobtriggers.kubeless.io 339 | spec: 340 | group: kubeless.io 341 | names: 342 | kind: CronJobTrigger 343 | plural: cronjobtriggers 344 | singular: cronjobtrigger 345 | scope: Namespaced 346 | version: v1beta1 347 | -------------------------------------------------------------------------------- /infra/kubeless/nats-v1.0.0-alpha.9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1beta1 3 | kind: Deployment 4 | metadata: 5 | labels: 6 | kubeless: nats-trigger-controller 7 | name: nats-trigger-controller 8 | namespace: kubeless 9 | spec: 10 | selector: 11 | matchLabels: 12 | kubeless: nats-trigger-controller 13 | template: 14 | metadata: 15 | labels: 16 | kubeless: nats-trigger-controller 17 | spec: 18 | containers: 19 | - env: 20 | - name: KUBELESS_NAMESPACE 21 | valueFrom: 22 | fieldRef: 23 | fieldPath: metadata.namespace 24 | - name: NATS_URL 25 | valueFrom: 26 | secretKeyRef: 27 | name: common-env-secrets 28 | key: SPLAT_NATS_URL 29 | - name: KUBELESS_CONFIG 30 | value: kubeless-config 31 | image: bitnami/nats-trigger-controller:v1.0.0-alpha.9 32 | imagePullPolicy: IfNotPresent 33 | name: nats-trigger-controller 34 | serviceAccountName: controller-acct 35 | --- 36 | apiVersion: rbac.authorization.k8s.io/v1beta1 37 | kind: ClusterRole 38 | metadata: 39 | name: nats-controller-deployer 40 | rules: 41 | - apiGroups: 42 | - "" 43 | resources: 44 | - services 45 | - configmaps 46 | verbs: 47 | - get 48 | - list 49 | - apiGroups: 50 | - kubeless.io 51 | resources: 52 | - functions 53 | - natstriggers 54 | verbs: 55 | - get 56 | - list 57 | - watch 58 | - update 59 | - delete 60 | --- 61 | apiVersion: rbac.authorization.k8s.io/v1beta1 62 | kind: ClusterRoleBinding 63 | metadata: 64 | name: nats-controller-deployer 65 | roleRef: 66 | apiGroup: rbac.authorization.k8s.io 67 | kind: ClusterRole 68 | name: nats-controller-deployer 69 | subjects: 70 | - kind: ServiceAccount 71 | name: controller-acct 72 | namespace: kubeless 73 | --- 74 | apiVersion: apiextensions.k8s.io/v1beta1 75 | kind: CustomResourceDefinition 76 | metadata: 77 | name: natstriggers.kubeless.io 78 | spec: 79 | group: kubeless.io 80 | names: 81 | kind: NATSTrigger 82 | plural: natstriggers 83 | singular: natstrigger 84 | scope: Namespaced 85 | version: v1beta1 86 | -------------------------------------------------------------------------------- /infra/nmap-workflow/nmap-workflow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nmap-workflow 5 | labels: 6 | app: nmap 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: nmap 12 | template: 13 | metadata: 14 | labels: 15 | app: nmap 16 | spec: 17 | containers: 18 | - name: nmap-ctr 19 | imagePullPolicy: Always 20 | image: appsecco/splat-workshop-nmap 21 | command: ["/usr/bin/splat-sidecar"] 22 | env: 23 | - name: SPLAT_MINIO_ENDPOINT 24 | valueFrom: 25 | secretKeyRef: 26 | name: common-env-secrets 27 | key: SPLAT_MINIO_ENDPOINT 28 | - name: SPLAT_MINIO_ACCESS_KEY 29 | valueFrom: 30 | secretKeyRef: 31 | name: common-env-secrets 32 | key: SPLAT_MINIO_ACCESS_KEY 33 | - name: SPLAT_MINIO_SECRET_KEY 34 | valueFrom: 35 | secretKeyRef: 36 | name: common-env-secrets 37 | key: SPLAT_MINIO_SECRET_KEY 38 | - name: MINIO_OUTPUT_BUCKET 39 | valueFrom: 40 | secretKeyRef: 41 | name: common-env-secrets 42 | key: MINIO_OUTPUT_BUCKET 43 | - name: SPLAT_NATS_URL 44 | valueFrom: 45 | secretKeyRef: 46 | name: common-env-secrets 47 | key: SPLAT_NATS_URL 48 | - name: SPLAT_MINIO_FILE_PATTERN 49 | value: scans/{{SCAN_ID}}/{{OUTPUT_EVENT}}/data.xml 50 | - name: SPLAT_MINIO_EVENT_NAME 51 | value: nmap 52 | - name: SPLAT_NATS_CONSUMER_TOPIC 53 | value: nmap-input 54 | - name: SPLAT_EXEC_TIMEOUT 55 | value: "1000" 56 | - name: SPLAT_EXEC_PATTERN 57 | value: "nmap --host-timeout 10 -Pn --open --webxml --top-ports 100 -oX {{OUTPUT_FILE_PATH}} {{TARGET}}" 58 | -------------------------------------------------------------------------------- /infra/report-generator/report-generator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: report-gen-workflow 5 | labels: 6 | app: report-gen 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: report-gen 12 | template: 13 | metadata: 14 | labels: 15 | app: report-gen 16 | spec: 17 | containers: 18 | - name: report-gen-ctr 19 | imagePullPolicy: Always 20 | image: appsecco/splat-workshop-report-generator 21 | command: ["./splat-sidecar"] 22 | volumeMounts: 23 | - name: googlesatoken 24 | mountPath: "/opt/secret/" 25 | readOnly: true 26 | env: 27 | - name: GOOGLE_STORAGE_BUCKET_BASE_URI 28 | value: gs://splat-reports/reports 29 | - name: SPLAT_MINIO_ENDPOINT 30 | valueFrom: 31 | secretKeyRef: 32 | name: common-env-secrets 33 | key: SPLAT_MINIO_ENDPOINT 34 | - name: SPLAT_MINIO_ACCESS_KEY 35 | valueFrom: 36 | secretKeyRef: 37 | name: common-env-secrets 38 | key: SPLAT_MINIO_ACCESS_KEY 39 | - name: SPLAT_MINIO_SECRET_KEY 40 | valueFrom: 41 | secretKeyRef: 42 | name: common-env-secrets 43 | key: SPLAT_MINIO_SECRET_KEY 44 | - name: MINIO_OUTPUT_BUCKET 45 | valueFrom: 46 | secretKeyRef: 47 | name: common-env-secrets 48 | key: MINIO_OUTPUT_BUCKET 49 | - name: SPLAT_NATS_URL 50 | valueFrom: 51 | secretKeyRef: 52 | name: common-env-secrets 53 | key: SPLAT_NATS_URL 54 | - name: SPLAT_MINIO_FILE_PATTERN 55 | value: scans/{{SCAN_ID}}/{{OUTPUT_EVENT}}/site-{{TIMESTAMP}}.tar.gz 56 | - name: SPLAT_MINIO_EVENT_NAME 57 | value: report-gen 58 | - name: SPLAT_NATS_CONSUMER_TOPIC 59 | value: report-gen-input 60 | - name: SPLAT_QUEUE_GROUP_NAME 61 | value: report-gen-tool 62 | - name: SPLAT_EXEC_PATTERN 63 | value: "./handler.sh {{TARGET}} {{OUTPUT_FILE_PATH}}" 64 | volumes: 65 | - name: googlesatoken 66 | secret: 67 | secretName: googlesatoken 68 | -------------------------------------------------------------------------------- /infra/zap-workflow/zap-workflow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: owasp-zap-workflow 5 | labels: 6 | app: owasp-zap 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: owasp-zap 12 | template: 13 | metadata: 14 | labels: 15 | app: owasp-zap 16 | spec: 17 | containers: 18 | - name: owasp-zap-ctr 19 | imagePullPolicy: Always 20 | image: appsecco/splat-workshop-owasp-zap 21 | command: ["/usr/bin/splat-sidecar"] 22 | env: 23 | - name: SPLAT_MINIO_ENDPOINT 24 | valueFrom: 25 | secretKeyRef: 26 | name: common-env-secrets 27 | key: SPLAT_MINIO_ENDPOINT 28 | - name: SPLAT_MINIO_ACCESS_KEY 29 | valueFrom: 30 | secretKeyRef: 31 | name: common-env-secrets 32 | key: SPLAT_MINIO_ACCESS_KEY 33 | - name: SPLAT_MINIO_SECRET_KEY 34 | valueFrom: 35 | secretKeyRef: 36 | name: common-env-secrets 37 | key: SPLAT_MINIO_SECRET_KEY 38 | - name: MINIO_OUTPUT_BUCKET 39 | valueFrom: 40 | secretKeyRef: 41 | name: common-env-secrets 42 | key: MINIO_OUTPUT_BUCKET 43 | - name: SPLAT_NATS_URL 44 | valueFrom: 45 | secretKeyRef: 46 | name: common-env-secrets 47 | key: SPLAT_NATS_URL 48 | - name: SPLAT_MINIO_FILE_PATTERN 49 | value: scans/{{SCAN_ID}}/{{OUTPUT_EVENT}}/data-{{TIMESTAMP}}.json 50 | - name: SPLAT_MINIO_EVENT_NAME 51 | value: owasp-zap 52 | - name: SPLAT_USE_OUTPUT_FILE_PATH 53 | value: /zap/wrk/data.json 54 | - name: SPLAT_NATS_CONSUMER_TOPIC 55 | value: owasp-zap-input 56 | - name: SPLAT_QUEUE_GROUP_NAME 57 | value: owasp-zap-tool 58 | - name: SPLAT_EXEC_PATTERN 59 | value: "rm /zap/wrk/data.json; /zap/zap-baseline.py -t {{TARGET}} -g gen.conf -J data.json; exit 0" -------------------------------------------------------------------------------- /presentation/Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/presentation/Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pdf -------------------------------------------------------------------------------- /presentation/Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appsecco/using-docker-kubernetes-for-automating-appsec-and-osint-workflows/6f782d8f183ae9ced382131e07191b728a7d1c50/presentation/Using-Docker-Kubernetes-for-Automating-AppSec-and-OSINT-Workflows-Presentation.pptx --------------------------------------------------------------------------------