diff --git a/components/producers/zaproxy/README.md b/components/producers/zaproxy/README.md index 8a9807e68..462ecf707 100644 --- a/components/producers/zaproxy/README.md +++ b/components/producers/zaproxy/README.md @@ -1,7 +1,10 @@ # OWASP Zaproxy producer This producer runs owasp zaproxy using its [automation framework](https://www.zaproxy.org/docs/desktop/addons/automation-framework/) capabilities. -The producer takes one argument `producer-owasp-zaproxy-automation-framework-file-base64` which accepts a file generated by `zap.sh -cmd -autogenmin ` or `zap.sh -cmd -autogenmax `, customized with the user's use case and encoded in base64 format. +The producer takes the following arguments, in order of preference and ignores the rest if multiples are provided: +* `producer-zaproxy-automation-framework-file-base64` : accepts a file generated by `zap.sh -cmd -autogenmin ` or `zap.sh -cmd -autogenmax `, customized with the user's values and encoded in base64 format +* ` producer-zaproxy-target` : accepts the target that will be passed to `zap.sh -quickurl` +* `producer-zaproxy-config-file-base64` : accept a base64 `config.xml` file from ZAP that will be passed to `zap.sh -configFile` be careful, zap configuration files can get very long and the base64 encoding of the file can exceed the Tekton variable limits. It is strongly advisable to limit the contents to only what you need. ## Automation Report section diff --git a/components/producers/zaproxy/kustomization.yaml b/components/producers/zaproxy/kustomization.yaml index 3c96611cc..650148024 100644 --- a/components/producers/zaproxy/kustomization.yaml +++ b/components/producers/zaproxy/kustomization.yaml @@ -16,23 +16,32 @@ patches: workspaces: - name: source-code-ws tasks: - - name: producer-owasp-zaproxy + - name: producer-zaproxy taskRef: - name: producer-owasp-zaproxy + name: producer-zaproxy workspaces: - name: source-code-ws workspace: source-code-ws params: - - name: producer-owasp-zaproxy-flags - value: - - $(params.producer-owasp-zaproxy-flags) - - name: producer-owasp-zaproxy-automation-framework-file-base64 - value: $(params.producer-owasp-zaproxy-automation-framework-file-base64) + - name: producer-zaproxy-target + value: $(params.producer-zaproxy-target) + - name: producer-zaproxy-config-file-base64 + value: $(params.producer-zaproxy-config-file-base64) + - name: producer-zaproxy-flags + value: $(params.producer-zaproxy-flags) + - name: producer-zaproxy-automation-framework-file-base64 + value: $(params.producer-zaproxy-automation-framework-file-base64) params: - - name: producer-owasp-zaproxy-flags - type: array - default: [] - - name: producer-owasp-zaproxy-automation-framework-file-base64 + - name: producer-zaproxy-target + type: string + default: "" + - name: producer-zaproxy-config-file-base64 + type: string + default: "" + - name: producer-zaproxy-flags + type: string + default: "" + - name: producer-zaproxy-automation-framework-file-base64 type: string default: "" target: @@ -42,7 +51,7 @@ patches: apiVersion: tekton.dev/v1beta1 kind: Task metadata: - name: producer-owasp-zaproxy + name: producer-zaproxy labels: v1.dracon.ocurity.com/component: producer spec: @@ -60,7 +69,7 @@ patches: script: echo "$(context.task.name)" > "$(results.anchor.path)" target: kind: Task - name: producer-owasp-zaproxy + name: producer-zaproxy # If we have a `source` task in the pipeline (added by a `source` component), # depend on the completion of that source by referencing its anchor. - patch: | @@ -70,7 +79,7 @@ patches: name: unused spec: tasks: - - name: producer-owasp-zaproxy + - name: producer-zaproxy params: - name: anchors value: @@ -92,7 +101,7 @@ patches: params: - name: anchors value: - - $(tasks.producer-owasp-zaproxy.results.anchor) + - $(tasks.producer-zaproxy.results.anchor) target: kind: Pipeline annotationSelector: v1.dracon.ocurity.com/has-producer-aggregator=true @@ -101,7 +110,7 @@ patches: apiVersion: tekton.dev/v1beta1 kind: Task metadata: - name: producer-owasp-zaproxy + name: producer-zaproxy labels: v1.dracon.ocurity.com/component: producer spec: @@ -113,12 +122,7 @@ patches: steps: - name: run-zap image: docker.io/owasp/zap2docker-stable:2.12.0 - script: | - #! /bin/bash - set -ex - echo "$(params.producer-owasp-zaproxy-automation-framework-file-base64)" | base64 -d > automation.yaml - cat automation.yaml - zap.sh -cmd -autorun automation.yaml + script: "#! /bin/bash\n set -ex\n automation=\"$(params.producer-zaproxy-automation-framework-file-base64)\"\n configFile=\"$(params.producer-zaproxy-config-file-base64)\"\n if [ -z \"$automation\" ]; then \n if [ -z \"$configFile\" ]; then\n echo \"Running ZAP with args: $(params.producer-zaproxy-flags)\"\n zap.sh -cmd -silent -notel -quickout /zap/wrk/out.json -quickurl $(params.producer-zaproxy-target) $(params.producer-zaproxy-flags)\n else \n echo \"$configFile\" | base64 -d > configuration.xml\n zap.sh -cmd -silent -notel -quickout /zap/wrk/out.json -quickurl $(params.producer-zaproxy-target) -configFile configuration.xml\n $(p..arams.producer-zaproxy-flags)\n fi\n else \n echo \"$automation\" | base64 -d > automation.yaml\n cat automation.yaml\n zap.sh -cmd -autorun automation.yaml\n fi\n" env: - name: DRACON_SCAN_TIME value: $(params.dracon_scan_start_time) @@ -133,7 +137,7 @@ patches: value: $(params.dracon_scan_id) target: kind: Task - name: producer-owasp-zaproxy + name: producer-zaproxy - patch: | apiVersion: tekton.dev/v1beta1 kind: Pipeline @@ -141,7 +145,7 @@ patches: name: unused spec: tasks: - - name: producer-owasp-zaproxy + - name: producer-zaproxy params: - name: dracon_scan_id value: $(tasks.base.results.dracon-scan-id) diff --git a/components/producers/zaproxy/task.yaml b/components/producers/zaproxy/task.yaml index 62503e4b6..779f126df 100644 --- a/components/producers/zaproxy/task.yaml +++ b/components/producers/zaproxy/task.yaml @@ -2,15 +2,21 @@ apiVersion: tekton.dev/v1beta1 kind: Task metadata: - name: producer-owasp-zaproxy + name: producer-zaproxy labels: v1.dracon.ocurity.com/component: producer spec: params: - - name: producer-owasp-zaproxy-flags - type: array - default: [] - - name: producer-owasp-zaproxy-automation-framework-file-base64 + - name: producer-zaproxy-target + type: string + default: "" + - name: producer-zaproxy-config-file-base64 + type: string + default: "" + - name: producer-zaproxy-flags + type: string + default: "" + - name: producer-zaproxy-automation-framework-file-base64 type: string default: "" volumes: @@ -25,9 +31,22 @@ spec: script: | #! /bin/bash set -ex - echo "$(params.producer-owasp-zaproxy-automation-framework-file-base64)" | base64 -d > automation.yaml - cat automation.yaml - zap.sh -cmd -autorun automation.yaml + automation="$(params.producer-zaproxy-automation-framework-file-base64)" + configFile="$(params.producer-zaproxy-config-file-base64)" + if [ -z "$automation" ]; then + if [ -z "$configFile" ]; then + echo "Running ZAP with args: $(params.producer-zaproxy-flags)" + zap.sh -cmd -silent -notel -quickout /zap/wrk/out.json -quickurl $(params.producer-zaproxy-target) $(params.producer-zaproxy-flags) + else + echo "$configFile" | base64 -d > configuration.xml + zap.sh -cmd -silent -notel -quickout /zap/wrk/out.json -quickurl $(params.producer-zaproxy-target) -configFile configuration.xml + $(p..arams.producer-zaproxy-flags) + fi + else + echo "$automation" | base64 -d > automation.yaml + cat automation.yaml + zap.sh -cmd -autorun automation.yaml + fi volumeMounts: - mountPath: /zap/wrk name: scratch diff --git a/examples/pipelines/dast-project/pipelinerun/pipelinerun.yaml b/examples/pipelines/dast-project/pipelinerun/pipelinerun.yaml index 7d7a7d43c..ae8355461 100644 --- a/examples/pipelines/dast-project/pipelinerun/pipelinerun.yaml +++ b/examples/pipelines/dast-project/pipelinerun/pipelinerun.yaml @@ -14,7 +14,7 @@ spec: value: Lvbo+wAsW8Y4ENBA+lAikOwGTYAIXCQ49eRMEwClv94w63tMW4vIYH3JE3mZwh4A9rqMqgWCWTATAYFM5Ut5cg== # - name: producer-testsslsh-target-url # value: https://badssl.com/ - - name: producer-owasp-zaproxy-automation-framework-file-base64 + - name: producer-zaproxy-automation-framework-file-base64 value: "LS0tICMgT1dBU1AgWkFQIGF1dG9tYXRpb24gY29uZmlndXJhdGlvbiBmaWxlLCBmb3IgbW9yZSBkZXRhaWxzIHNlZSBodHRwczovL3d3dy56YXByb3h5Lm9yZy9kb2NzL2F1dG9tYXRlL2F1dG9tYXRpb24tZnJhbWV3b3JrLwplbnY6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICBjb250ZXh0cyA6CiAgICAtIG5hbWU6IGRyYWNvbiAgICAgICAgICAgICAgICAgICMgTmFtZSB0byBiZSB1c2VkIHRvIHJlZmVyIHRvIHRoaXMgY29udGV4dCBpbiBvdGhlciBqb2JzLCBtYW5kYXRvcnkKICAgICAgdXJsczogWyJodHRwczovL2N5YmVyZHluZS5kZW1vLmRyYWNvbi5jbG91ZC8iXQogIHZhcnM6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIExpc3Qgb2YgMCBvciBtb3JlIHZhcmlhYmxlcywgY2FuIGJlIHVzZWQgaW4gdXJscyBhbmQgc2VsZWN0ZWQgb3RoZXIgcGFyYW1ldGVycwogIHBhcmFtZXRlcnM6CiAgICBmYWlsT25FcnJvcjogZmFsc2UgICAgICAgICAgICAgICAgICAjIElmIHNldCBleGl0IG9uIGFuIGVycm9yICAgICAgICAgCiAgICBmYWlsT25XYXJuaW5nOiBmYWxzZSAgICAgICAgICAgICAgICMgSWYgc2V0IGV4aXQgb24gYSB3YXJuaW5nCiAgICBwcm9ncmVzc1RvU3Rkb3V0OiB0cnVlICAgICAgICAgICAgICMgSWYgc2V0IHdpbGwgd3JpdGUgam9iIHByb2dyZXNzIHRvIHN0ZG91dAoKam9iczoKICAtIHR5cGU6IHBhc3NpdmVTY2FuLWNvbmZpZyAgICAgICAgICAgIyBQYXNzaXZlIHNjYW4gY29uZmlndXJhdGlvbgogICAgcGFyYW1ldGVyczoKICAgICAgbWF4QWxlcnRzUGVyUnVsZTogMTAwICAgICAgICAgICAgICMgSW50OiBNYXhpbXVtIG51bWJlciBvZiBhbGVydHMgdG8gcmFpc2UgcGVyIHJ1bGUKICAgICAgc2Nhbk9ubHlJblNjb3BlOiB0cnVlICAgICAgICAgICAgIyBCb29sOiBPbmx5IHNjYW4gVVJMcyBpbiBzY29wZSAocmVjb21tZW5kZWQpCiAgICAgIG1heEJvZHlTaXplSW5CeXRlc1RvU2NhbjogMCAgICAgICAjIEludDogTWF4aW11bSBib2R5IHNpemUgdG8gc2NhbiwgZGVmYXVsdDogMCAtIHdpbGwgc2NhbiBhbGwgbWVzc2FnZXMKICAtIHR5cGU6IHNwaWRlciAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdHJhZGl0aW9uYWwgc3BpZGVyIC0gZmFzdCBidXQgZG9lc250IGhhbmRsZSBtb2Rlcm4gYXBwcyBzbyB3ZWxsCiAgICBwYXJhbWV0ZXJzOgogICAgICBjb250ZXh0OiAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0cmluZzogTmFtZSBvZiB0aGUgY29udGV4dCB0byBzcGlkZXIsIGRlZmF1bHQ6IGZpcnN0IGNvbnRleHQKICAgICAgdXNlcjogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IEFuIG9wdGlvbmFsIHVzZXIgdG8gdXNlIGZvciBhdXRoZW50aWNhdGlvbiwgbXVzdCBiZSBkZWZpbmVkIGluIHRoZSBlbnYKICAgICAgdXJsOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IFVybCB0byBzdGFydCBzcGlkZXJpbmcgZnJvbSwgZGVmYXVsdDogZmlyc3QgY29udGV4dCBVUkwKICAgICAgbWF4RHVyYXRpb246ICAgICAgICAgICAgICAgICAgICAgIyBJbnQ6IFRoZSBtYXggdGltZSBpbiBtaW51dGVzIHRoZSBzcGlkZXIgd2lsbCBiZSBhbGxvd2VkIHRvIHJ1biBmb3IsIGRlZmF1bHQ6IDAgdW5saW1pdGVkCiAgICAgIG1heERlcHRoOiAgICAgICAgICAgICAgICAgICAgICAgICMgSW50OiBUaGUgbWF4aW11bSB0cmVlIGRlcHRoIHRvIGV4cGxvcmUKICAgICAgbWF4Q2hpbGRyZW46ICAgICAgICAgICAgICAgICAgICAgIyBJbnQ6IFRoZSBtYXhpbXVtIG51bWJlciBvZiBjaGlsZHJlbiB0byBhZGQgdG8gZWFjaCBub2RlIGluIHRoZSB0cmVlCiAgICB0ZXN0czoKICAgICAgLSBuYW1lOiAnQXQgbGVhc3QgWCBVUkxzIGZvdW5kJyAgICAgICAgICAgICAgICAgICAjIFN0cmluZzogTmFtZSBvZiB0aGUgdGVzdCwgZGVmYXVsdDogc3RhdGlzdGljICsgb3BlcmF0b3IgKyB2YWx1ZQogICAgICAgIHR5cGU6ICdzdGF0cycgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3RyaW5nOiBUeXBlIG9mIHRlc3QsIG9ubHkgJ3N0YXRzJyBpcyBzdXBwb3J0ZWQgZm9yIG5vdwogICAgICAgIHN0YXRpc3RpYzogJ2F1dG9tYXRpb24uc3BpZGVyLnVybHMuYWRkZWQnICAgICAgICMgU3RyaW5nOiBOYW1lIG9mIGFuIGludGVnZXIgLyBsb25nIHN0YXRpc3RpYywgY3VycmVudGx5IHN1cHBvcnRlZDogJ2F1dG9tYXRpb24uc3BpZGVyLnVybHMuYWRkZWQnCiAgICAgICAgb3BlcmF0b3I6ICc+PScgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmcgWyc9PScsICchPScsICc+PScsICc+JywgJzwnLCAnPD0nXTogT3BlcmF0b3IgdXNlZCBmb3IgdGVzdGluZwogICAgICAgIHZhbHVlOiAxMDAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSW50OiBDaGFuZ2UgdGhpcyB0byB0aGUgbnVtYmVyIG9mIFVSTHMgeW91IGV4cGVjdCB0byBmaW5kCiAgICAgICAgb25GYWlsOiAnaW5mbycgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IE9uZSBvZiAnd2FybicsICdlcnJvcicsICdpbmZvJywgbWFuZGF0b3J5CiAgLSB0eXBlOiBzcGlkZXJBamF4ICAgICAgICAgICAgICAgICAgICMgVGhlIGFqYXggc3BpZGVyIC0gc2xvd2VyIHRoYW4gdGhlIHN0YW5kYXJkIHNwaWRlciBidXQgaGFuZGxlcyBtb2Rlcm4gYXBwcyB3ZWxsCiAgICBwYXJhbWV0ZXJzOgogICAgICBjb250ZXh0OiAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0cmluZzogTmFtZSBvZiB0aGUgY29udGV4dCB0byBzcGlkZXIsIGRlZmF1bHQ6IGZpcnN0IGNvbnRleHQKICAgICAgdXJsOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IFVybCB0byBzdGFydCBzcGlkZXJpbmcgZnJvbSwgZGVmYXVsdDogZmlyc3QgY29udGV4dCBVUkwKICAgICAgbWF4RHVyYXRpb246ICAgICAgICAgICAgICAgICAgICAgIyBJbnQ6IFRoZSBtYXggdGltZSBpbiBtaW51dGVzIHRoZSBhamF4IHNwaWRlciB3aWxsIGJlIGFsbG93ZWQgdG8gcnVuIGZvciwgZGVmYXVsdDogMCB1bmxpbWl0ZWQKICAgICAgbWF4Q3Jhd2xEZXB0aDogICAgICAgICAgICAgICAgICAgIyBJbnQ6IFRoZSBtYXggZGVwdGggdGhhdCB0aGUgY3Jhd2xlciBjYW4gcmVhY2gsIGRlZmF1bHQ6IDEwLCAwIGlzIHVubGltaXRlZAogICAgICBudW1iZXJPZkJyb3dzZXJzOiAgICAgICAgICAgICAgICAjIEludDogVGhlIG51bWJlciBvZiBicm93c2VycyB0aGUgc3BpZGVyIHdpbGwgdXNlLCBtb3JlIHdpbGwgYmUgZmFzdGVyIGJ1dCB3aWxsIHVzZSB1cCBtb3JlIG1lbW9yeSwgZGVmYXVsdDogMQogICAgICBydW5Pbmx5SWZNb2Rlcm46ICAgICAgICAgICAgICAgICAjIEJvb2xlYW46IElmIHRydWUgdGhlbiB0aGUgc3BpZGVyIHdpbGwgb25seSBydW4gaWYgYSAibW9kZXJuIGFwcCIgYWxlcnQgaXMgcmFpc2VkLCBkZWZhdWx0OiBmYWxzZQogICAgdGVzdHM6CiAgICAgIC0gbmFtZTogJ0F0IGxlYXN0IFggVVJMcyBmb3VuZCcgICAgICAgICMgU3RyaW5nOiBOYW1lIG9mIHRoZSB0ZXN0LCBkZWZhdWx0OiBzdGF0aXN0aWMgKyBvcGVyYXRvciArIHZhbHVlCiAgICAgICAgdHlwZTogJ3N0YXRzJyAgICAgICAgICAgICAgICAgICAgICAgICMgU3RyaW5nOiBUeXBlIG9mIHRlc3QsIG9ubHkgJ3N0YXRzJyBpcyBzdXBwb3J0ZWQgZm9yIG5vdwogICAgICAgIHN0YXRpc3RpYzogJ3NwaWRlckFqYXgudXJscy5hZGRlZCcgICAjIFN0cmluZzogTmFtZSBvZiBhbiBpbnRlZ2VyIC8gbG9uZyBzdGF0aXN0aWMsIGN1cnJlbnRseSBzdXBwb3J0ZWQ6ICdzcGlkZXJBamF4LnVybHMuYWRkZWQnCiAgICAgICAgb3BlcmF0b3I6ICc+PScgICAgICAgICAgICAgICAgICAgICAgICMgU3RyaW5nIFsnPT0nLCAnIT0nLCAnPj0nLCAnPicsICc8JywgJzw9J106IE9wZXJhdG9yIHVzZWQgZm9yIHRlc3RpbmcKICAgICAgICB2YWx1ZTogMTAwICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJbnQ6IENoYW5nZSB0aGlzIHRvIHRoZSBudW1iZXIgb2YgVVJMcyB5b3UgZXhwZWN0IHRvIGZpbmQKICAgICAgICBvbkZhaWw6ICdpbmZvJyAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmcgW3dhcm4sIGVycm9yLCBpbmZvXTogQ2hhbmdlIHRoaXMgdG8gJ3dhcm4nIG9yICdlcnJvcicgZm9yIHRoZSB0ZXN0IHRvIHRha2UgZWZmZWN0CiAgLSB0eXBlOiBkZWxheSAgICAgICAgICAgICAgICAgICAgICAgICMgUGF1c2UgdGhlIHBsYW4gZm9yIGEgc2V0IHBlcmlvZCBvZiB0aW1lIG9yIGV2ZW50IChmaWxlIGNyZWF0ZWQsIHByb2dyYW1tYXRpYyBtZXRob2QgY2FsbGVkLCBBUEkgZW5kcG9pbnQgY2FsbGVkKQogICAgcGFyYW1ldGVyczoKICAgICAgdGltZTogMSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3RyaW5nOiBUaGUgdGltZSB0byB3YWl0LCBmb3JtYXQgYW55IG9mIFsnaGg6bW06c3MnLCAnbW06c3MnLCAnc3MnXSwgZGVmYXVsdDogMAogICAgICBmaWxlTmFtZTogICAgICAgICAgICAgICAgICAgICAgICAjIFN0cmluZzogTmFtZSBvZiBhIGZpbGUgd2hpY2ggd2lsbCBjYXVzZSB0aGUgam9iIHRvIGVuZCBlYXJseSBpZiBjcmVhdGVkLCBkZWZhdWx0OiBlbXB0eQogIC0gdHlwZTogcGFzc2l2ZVNjYW4td2FpdCAgICAgICAgICAgICAjIFBhc3NpdmUgc2NhbiB3YWl0IGZvciB0aGUgcGFzc2l2ZSBzY2FubmVyIHRvIGZpbmlzaAogICAgcGFyYW1ldGVyczoKICAgICAgbWF4RHVyYXRpb246IDUgICAgICAgICAgICAgICAgICAgIyBJbnQ6IFRoZSBtYXggdGltZSB0byB3YWl0IGZvciB0aGUgcGFzc2l2ZSBzY2FubmVyLCBkZWZhdWx0OiAwIHVubGltaXRlZAogIC0gdHlwZTogYWN0aXZlU2NhbiAgICAgICAgICAgICAgICAgICAjIFRoZSBhY3RpdmUgc2Nhbm5lciAtIHRoaXMgYWN0aXZlbHkgYXR0YWNrcyB0aGUgdGFyZ2V0IHNvIHNob3VsZCBvbmx5IGJlIHVzZWQgd2l0aCBwZXJtaXNzaW9uCiAgICBwYXJhbWV0ZXJzOgogICAgICBjb250ZXh0OiBkcmFjb24gICAgICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IE5hbWUgb2YgdGhlIGNvbnRleHQgdG8gYXR0YWNrLCBkZWZhdWx0OiBmaXJzdCBjb250ZXh0CiAgLSB0eXBlOiByZXBvcnQgICAgICAgICAgICAgICAgICAgICAgICMgUmVwb3J0IGdlbmVyYXRpb24KICAgIHBhcmFtZXRlcnM6CiAgICAgIHRlbXBsYXRlOiB0cmFkaXRpb25hbC1qc29uCiAgICAgIHJlcG9ydERpcjogL3phcC93cmsgICAgICAgICAgICAgICAgICAgICAgICMgU3RyaW5nOiBUaGUgZGlyZWN0b3J5IGludG8gd2hpY2ggdGhlIHJlcG9ydCB3aWxsIGJlIHdyaXR0ZW4KICAgICAgcmVwb3J0RmlsZTogb3V0Lmpzb24gICAgICAgICAgICAgICAgICAgICAgIyBTdHJpbmc6IFRoZSByZXBvcnQgZmlsZSBuYW1lIHBhdHRlcm4sIGRlZmF1bHQ6IHt7eXl5eS1NTS1kZH19LVpBUC1SZXBvcnQtW1tzaXRlXV0K" workspaces: - name: source-code-ws