From e4230cdc8e0fbc237b00511acdc6e3720338cad2 Mon Sep 17 00:00:00 2001 From: Venera Bikbulatova Date: Tue, 9 Jun 2026 20:51:31 +0600 Subject: [PATCH 1/4] docs: add universal PR template Signed-off-by: Venera Bikbulatova --- .github/pull_request_template.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..e157c6a2b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Goal + + +## Changes +- + +## Testing + + +## Checklist +- [ ] Title is a clear sentence (≤ 70 chars) +- [ ] Commits are signed (git log --show-signature) +- [ ] submissions/labN.md updated From 09dae0a2f20c8d011829c4c740c5ca7ebeb113bf Mon Sep 17 00:00:00 2001 From: Venera Bikbulatova Date: Tue, 9 Jun 2026 22:23:15 +0600 Subject: [PATCH 2/4] docs: upstream moved while you worked Signed-off-by: Venera Bikbulatova From b99da83bbdb15a0adc93af55f42cb2b6baa17486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=20=D0=91=D0=B8=D0=BA?= =?UTF-8?q?=D0=B1=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2=D0=B0?= Date: Tue, 7 Jul 2026 21:53:51 +0300 Subject: [PATCH 3/4] Lab 9: Add security headers middleware, Trivy scans, ZAP baseline --- Dockerfile | 15 + app/main.go | 7 +- app/middleware/security.go | 17 ++ app/middleware/security_test.go | 35 +++ submissions/sbom-cyclonedx.json | 437 ++++++++++++++++++++++++++++++ submissions/trivy-config-scan.txt | 0 submissions/trivy-fs-scan.txt | 16 ++ submissions/trivy-image-scan.txt | 52 ++++ submissions/zap.yaml | 41 +++ 9 files changed, 619 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 app/middleware/security.go create mode 100644 app/middleware/security_test.go create mode 100644 submissions/sbom-cyclonedx.json create mode 100644 submissions/trivy-config-scan.txt create mode 100644 submissions/trivy-fs-scan.txt create mode 100644 submissions/trivy-image-scan.txt create mode 100644 submissions/zap.yaml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..d9dbe2565 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.24-alpine AS builder + +WORKDIR /app +COPY app/go.mod . +RUN go mod download +COPY app/ . +RUN go build -o quicknotes . + +FROM alpine:3.21 +RUN apk add --no-cache ca-certificates +WORKDIR /app +COPY --from=builder /app/quicknotes . +COPY app/seed.json . +EXPOSE 8080 +CMD ["./quicknotes"] diff --git a/app/main.go b/app/main.go index e258ffcfe..108fdb02c 100644 --- a/app/main.go +++ b/app/main.go @@ -9,6 +9,8 @@ import ( "os/signal" "syscall" "time" + + "quicknotes/middleware" ) func main() { @@ -26,9 +28,12 @@ func main() { } server := NewServer(store) + + handler := middleware.SecurityHeadersMiddleware(server.Routes()) + srv := &http.Server{ Addr: addr, - Handler: server.Routes(), + Handler: handler, ReadHeaderTimeout: 5 * time.Second, } diff --git a/app/middleware/security.go b/app/middleware/security.go new file mode 100644 index 000000000..a3c188dd0 --- /dev/null +++ b/app/middleware/security.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "net/http" +) + +func SecurityHeadersMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("X-XSS-Protection", "1; mode=block") + w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'") + + next.ServeHTTP(w, r) + }) +} diff --git a/app/middleware/security_test.go b/app/middleware/security_test.go new file mode 100644 index 000000000..802b2f940 --- /dev/null +++ b/app/middleware/security_test.go @@ -0,0 +1,35 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestSecurityHeadersMiddleware(t *testing.T) { + handler := SecurityHeadersMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, req) + + resp := w.Result() + defer resp.Body.Close() + + headers := map[string]string{ + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + "X-XSS-Protection": "1; mode=block", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'; frame-ancestors 'none'", + } + + for header, expected := range headers { + actual := resp.Header.Get(header) + if actual != expected { + t.Errorf("Header %s = %s, expected %s", header, actual, expected) + } + } +} diff --git a/submissions/sbom-cyclonedx.json b/submissions/sbom-cyclonedx.json new file mode 100644 index 000000000..cb7a4602d --- /dev/null +++ b/submissions/sbom-cyclonedx.json @@ -0,0 +1,437 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:bbc28fc2-7647-47d7-a279-1b10ae611337", + "version": 1, + "metadata": { + "timestamp": "2026-07-07T18:32:17+00:00", + "tools": { + "components": [ + { + "type": "application", + "group": "aquasecurity", + "name": "trivy", + "version": "0.58.2" + } + ] + }, + "component": { + "bom-ref": "a6bf74f1-a2ba-4028-a017-49f8fb69cf2f", + "type": "container", + "name": "quicknotes:lab6", + "properties": [ + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:187cfc6d1e3e8a40a5e64653bcd3239c140807dcf1c09e48021178705a5a6139" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:275a30dd8ce958b21daa9ad962c6fbc09f98306ee2f486b65c9075dc257b1412" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:4cde6b0bb6f50a5f255eef7b2a42162c661cf776b803225dcac9a659e396bb6b" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:4d049f83d9cf21d1f5cc0e11deaf36df02790d0e60c1a3829538fb4b61685368" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:5fd2536c39c0700be8b7b4344e375196da2f126842fd8ede66996a18860a3890" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:621c35e751a51a9a9dc3e80aa0b7fe8be2a93402ea6ccd307d30852cd7776cda" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:6f1cdceb6a3146f0ccb986521156bef8a422cdbb0863396f7f751f575ba308f4" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:92cb9c37b7d3957ac56645a979418f65e6c5bdba00eb99622affae5fc124ac07" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:ad51d0769d16ba578106a177987dfe3d2e02c1668c852b795b2f6b024068242a" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:af5aa97ebe6ce1604747ec1e21af7136ded391bcabe4acef882e718a87c86bcc" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:b47e820efda8baf924a0d9b06581a23730241a308aa2ad49da66fb446e8205ef" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:bd3cdfae1d3fdd83a2231d608969b38b82349777c2fff9a7c12d54f8ac5c9b38" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:bec7e6bb35e05d1284f28b10d2150c259717d91c658c4c10c08424bb9466caba" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:c8b007d0206e4b10ed4d3b3d99dfeab47c2648e82011989fd78a5731baf33fc3" + }, + { + "name": "aquasecurity:trivy:ImageID", + "value": "sha256:deb2af6094dd59d4f203f98ae111cc197cb3656973fbe2751229b7f8280db3ad" + }, + { + "name": "aquasecurity:trivy:Labels:com.docker.compose.image.builder", + "value": "classic" + }, + { + "name": "aquasecurity:trivy:RepoTag", + "value": "quicknotes:lab6" + }, + { + "name": "aquasecurity:trivy:SchemaVersion", + "value": "2" + } + ] + } + }, + "components": [ + { + "bom-ref": "4f0f9be1-a02a-4c45-9b0b-fd55c075a880", + "type": "operating-system", + "name": "debian", + "version": "13.5", + "properties": [ + { + "name": "aquasecurity:trivy:Class", + "value": "os-pkgs" + }, + { + "name": "aquasecurity:trivy:Type", + "value": "debian" + } + ] + }, + { + "bom-ref": "af3cdd99-c9fe-45dc-9579-e25cd229f300", + "type": "application", + "name": "app/quicknotes", + "properties": [ + { + "name": "aquasecurity:trivy:Class", + "value": "lang-pkgs" + }, + { + "name": "aquasecurity:trivy:Type", + "value": "gobinary" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/base-files@13.8%2Bdeb13u5?arch=amd64&distro=debian-13.5", + "type": "library", + "supplier": { + "name": "Santiago Vila " + }, + "name": "base-files", + "version": "13.8+deb13u5", + "licenses": [ + { + "license": { + "name": "GPL-2.0-or-later" + } + }, + { + "license": { + "name": "verbatim" + } + } + ], + "purl": "pkg:deb/debian/base-files@13.8%2Bdeb13u5?arch=amd64&distro=debian-13.5", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:92cb9c37b7d3957ac56645a979418f65e6c5bdba00eb99622affae5fc124ac07" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "base-files@13.8+deb13u5" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "base-files" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "13.8+deb13u5" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/media-types@13.0.0?arch=all&distro=debian-13.5", + "type": "library", + "supplier": { + "name": "Mime-Support Packagers " + }, + "name": "media-types", + "version": "13.0.0", + "licenses": [ + { + "license": { + "name": "ad-hoc" + } + } + ], + "purl": "pkg:deb/debian/media-types@13.0.0?arch=all&distro=debian-13.5", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:275a30dd8ce958b21daa9ad962c6fbc09f98306ee2f486b65c9075dc257b1412" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "media-types@13.0.0" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "media-types" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "13.0.0" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/netbase@6.5?arch=all&distro=debian-13.5", + "type": "library", + "supplier": { + "name": "Marco d'Itri " + }, + "name": "netbase", + "version": "6.5", + "licenses": [ + { + "license": { + "name": "GPL-2.0-only" + } + } + ], + "purl": "pkg:deb/debian/netbase@6.5?arch=all&distro=debian-13.5", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:621c35e751a51a9a9dc3e80aa0b7fe8be2a93402ea6ccd307d30852cd7776cda" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "netbase@6.5" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "netbase" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "6.5" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/tzdata-legacy@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "type": "library", + "supplier": { + "name": "GNU Libc Maintainers " + }, + "name": "tzdata-legacy", + "version": "2026b-0+deb13u1", + "licenses": [ + { + "license": { + "name": "public-domain" + } + } + ], + "purl": "pkg:deb/debian/tzdata-legacy@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:bec7e6bb35e05d1284f28b10d2150c259717d91c658c4c10c08424bb9466caba" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "tzdata-legacy@2026b-0+deb13u1" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "tzdata" + }, + { + "name": "aquasecurity:trivy:SrcRelease", + "value": "0+deb13u1" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "2026b" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/tzdata@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "type": "library", + "supplier": { + "name": "GNU Libc Maintainers " + }, + "name": "tzdata", + "version": "2026b-0+deb13u1", + "licenses": [ + { + "license": { + "name": "public-domain" + } + } + ], + "purl": "pkg:deb/debian/tzdata@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:c8b007d0206e4b10ed4d3b3d99dfeab47c2648e82011989fd78a5731baf33fc3" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "tzdata@2026b-0+deb13u1" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "tzdata" + }, + { + "name": "aquasecurity:trivy:SrcRelease", + "value": "0+deb13u1" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "2026b" + } + ] + }, + { + "bom-ref": "pkg:golang/quicknotes", + "type": "library", + "name": "quicknotes", + "purl": "pkg:golang/quicknotes", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:b47e820efda8baf924a0d9b06581a23730241a308aa2ad49da66fb446e8205ef" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "quicknotes" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "gobinary" + } + ] + }, + { + "bom-ref": "pkg:golang/stdlib@v1.24.13", + "type": "library", + "name": "stdlib", + "version": "v1.24.13", + "purl": "pkg:golang/stdlib@v1.24.13", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:b47e820efda8baf924a0d9b06581a23730241a308aa2ad49da66fb446e8205ef" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "stdlib@v1.24.13" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "gobinary" + } + ] + } + ], + "dependencies": [ + { + "ref": "4f0f9be1-a02a-4c45-9b0b-fd55c075a880", + "dependsOn": [ + "pkg:deb/debian/base-files@13.8%2Bdeb13u5?arch=amd64&distro=debian-13.5", + "pkg:deb/debian/media-types@13.0.0?arch=all&distro=debian-13.5", + "pkg:deb/debian/netbase@6.5?arch=all&distro=debian-13.5", + "pkg:deb/debian/tzdata-legacy@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "pkg:deb/debian/tzdata@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5" + ] + }, + { + "ref": "a6bf74f1-a2ba-4028-a017-49f8fb69cf2f", + "dependsOn": [ + "4f0f9be1-a02a-4c45-9b0b-fd55c075a880", + "af3cdd99-c9fe-45dc-9579-e25cd229f300" + ] + }, + { + "ref": "af3cdd99-c9fe-45dc-9579-e25cd229f300", + "dependsOn": [ + "pkg:golang/quicknotes" + ] + }, + { + "ref": "pkg:deb/debian/base-files@13.8%2Bdeb13u5?arch=amd64&distro=debian-13.5", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/media-types@13.0.0?arch=all&distro=debian-13.5", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/netbase@6.5?arch=all&distro=debian-13.5", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/tzdata-legacy@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/tzdata@2026b-0%2Bdeb13u1?arch=all&distro=debian-13.5", + "dependsOn": [] + }, + { + "ref": "pkg:golang/quicknotes", + "dependsOn": [ + "pkg:golang/stdlib@v1.24.13" + ] + }, + { + "ref": "pkg:golang/stdlib@v1.24.13", + "dependsOn": [] + } + ], + "vulnerabilities": [] +} diff --git a/submissions/trivy-config-scan.txt b/submissions/trivy-config-scan.txt new file mode 100644 index 000000000..e69de29bb diff --git a/submissions/trivy-fs-scan.txt b/submissions/trivy-fs-scan.txt new file mode 100644 index 000000000..155ee06f3 --- /dev/null +++ b/submissions/trivy-fs-scan.txt @@ -0,0 +1,16 @@ + +.vagrant/machines/default/virtualbox/private_key (secrets) +========================================================== +Total: 1 (HIGH: 1, CRITICAL: 0) + +HIGH: AsymmetricPrivateKey (private-key) +════════════════════════════════════════ +Asymmetric Private Key +──────────────────────────────────────── + .vagrant/machines/default/virtualbox/private_key:1 +──────────────────────────────────────── + 1 [ ----BEGIN RSA PRIVATE KEY-----******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************-----END RSA PRIVATE + 2 +──────────────────────────────────────── + + diff --git a/submissions/trivy-image-scan.txt b/submissions/trivy-image-scan.txt new file mode 100644 index 000000000..0ea7f0700 --- /dev/null +++ b/submissions/trivy-image-scan.txt @@ -0,0 +1,52 @@ + +quicknotes:lab6 (debian 13.5) +============================= +Total: 0 (HIGH: 0, CRITICAL: 0) + + +app/quicknotes (gobinary) +========================= +Total: 10 (HIGH: 10, CRITICAL: 0) + +┌─────────┬────────────────┬──────────┬────────┬───────────────────┬─────────────────┬──────────────────────────────────────────────────────────────┐ +│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │ +├─────────┼────────────────┼──────────┼────────┼───────────────────┼─────────────────┼──────────────────────────────────────────────────────────────┤ +│ stdlib │ CVE-2026-25679 │ HIGH │ fixed │ v1.24.13 │ 1.25.8, 1.26.1 │ net/url: Incorrect parsing of IPv6 host literals in net/url │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-25679 │ +│ ├────────────────┤ │ │ ├─────────────────┼──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-27145 │ │ │ │ 1.25.11, 1.26.4 │ crypto/x509: golang: golang crypto/x509: Denial of Service │ +│ │ │ │ │ │ │ via excessive processing of DNS... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-27145 │ +│ ├────────────────┤ │ │ ├─────────────────┼──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-32280 │ │ │ │ 1.25.9, 1.26.2 │ crypto/x509: crypto/tls: golang: Go: Denial of Service │ +│ │ │ │ │ │ │ vulnerability in certificate chain building... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-32280 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-32281 │ │ │ │ │ crypto/x509: golang: Go crypto/x509: Denial of Service via │ +│ │ │ │ │ │ │ inefficient certificate chain validation... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-32281 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-32283 │ │ │ │ │ crypto/tls: golang: Go crypto/tls: Denial of Service via │ +│ │ │ │ │ │ │ multiple TLS 1.3 key... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-32283 │ +│ ├────────────────┤ │ │ ├─────────────────┼──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-33811 │ │ │ │ 1.25.10, 1.26.3 │ net: golang: Go net package: Denial of Service via long │ +│ │ │ │ │ │ │ CNAME response... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-33811 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-33814 │ │ │ │ │ net/http/internal/http2: golang: golang.org/x/net: Go │ +│ │ │ │ │ │ │ HTTP/2: Denial of Service via malformed │ +│ │ │ │ │ │ │ SETTINGS_MAX_FRAME_SIZE frame... │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-33814 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-39820 │ │ │ │ │ net/mail: golang: Go net/mail: Denial of Service via crafted │ +│ │ │ │ │ │ │ email inputs │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-39820 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-39836 │ │ │ │ │ ELSA-2026-22121: golang security update (IMPORTANT) │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-39836 │ +│ ├────────────────┤ │ │ │ ├──────────────────────────────────────────────────────────────┤ +│ │ CVE-2026-42499 │ │ │ │ │ net/mail: golang: net/mail: Denial of Service via │ +│ │ │ │ │ │ │ pathological email address parsing │ +│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2026-42499 │ +└─────────┴────────────────┴──────────┴────────┴───────────────────┴─────────────────┴──────────────────────────────────────────────────────────────┘ diff --git a/submissions/zap.yaml b/submissions/zap.yaml new file mode 100644 index 000000000..5c1100f2c --- /dev/null +++ b/submissions/zap.yaml @@ -0,0 +1,41 @@ +env: + contexts: + - excludePaths: [] + name: baseline + urls: + - http://172.17.0.1:8080/health + - http://172.17.0.1:8080/ + parameters: + failOnError: true + progressToStdout: false +jobs: +- parameters: + enableTags: false + maxAlertsPerRule: 10 + type: passiveScan-config +- parameters: + maxDuration: 1 + url: http://172.17.0.1:8080/ + type: spider +- parameters: + maxDuration: 0 + type: passiveScan-wait +- parameters: + format: Long + summaryFile: /home/zap/zap_out.json + rules: [] + type: outputSummary +- parameters: + reportDescription: '' + reportDir: /zap/wrk/ + reportFile: /zap/wrk/zap-report-fixed.html + reportTitle: ZAP Scanning Report + template: traditional-html + type: report +- parameters: + reportDescription: '' + reportDir: /zap/wrk/ + reportFile: /zap/wrk/zap-report-fixed.json + reportTitle: ZAP Scanning Report + template: traditional-json + type: report From f04e2645f929851c5c4bd9d62eb46a9fb9f5c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=20=D0=91=D0=B8=D0=BA?= =?UTF-8?q?=D0=B1=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2=D0=B0?= Date: Sun, 19 Jul 2026 22:40:32 +0300 Subject: [PATCH 4/4] Add lab9 report --- submissions/lab9.md | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 submissions/lab9.md diff --git a/submissions/lab9.md b/submissions/lab9.md new file mode 100644 index 000000000..29c2862b9 --- /dev/null +++ b/submissions/lab9.md @@ -0,0 +1,75 @@ +Lab 9 — DevSecOps: Scan QuickNotes with Trivy + ZAP + +Task 1 — Trivy + +Image scan results: +10 HIGH vulnerabilities found in Go standard library (CVE-2026-25679, CVE-2026-27145, CVE-2026-32280, CVE-2026-32281, CVE-2026-32283, and 5 more related to TLS/crypto). All require Go upgrade to versions 1.26.1 or 1.26.4. Decision: FIX. + +Filesystem scan results: +1 HIGH finding: private key in .vagrant/machines/default/virtualbox/private_key. Decision: FIX - remove .vagrant directory from repository. + +Config scan results: +No HIGH or CRITICAL findings. + +SBOM generated successfully (cyclonedx format, 13KB). + +Design questions: + +a) CVE severity is just the starting point. When I triage findings I also need to understand if the vulnerable code is actually reachable from our application logic. If we don't call the vulnerable function, the risk is much lower. I look at whether there are public exploits and if the vulnerability is being actively used in attacks. Deployment context matters a lot - is this service exposed to the internet or only internal? The attack complexity matters too - some vulnerabilities require specific conditions or privileges that make them hard to exploit. Finally I consider the actual business impact if someone did exploit it. A critical vulnerability in a non-critical service might be less urgent than a high vulnerability in our main API. + +b) Distroless images are effective because they remove almost everything except the application itself. No package managers means attackers can't install new tools. No shell makes it harder to execute commands. No OS utilities like curl or wget means attackers can't download additional payloads. The attack surface shrinks dramatically because there are fewer packages to have vulnerabilities and fewer tools for attackers to use. The image size also decreases significantly which helps with scan times. + +c) I think .trivyignore is legitimate when you have a false positive that the scanner consistently flags incorrectly, or when you've accepted a risk with clear mitigation in place like a WAF or network isolation. You might also ignore vulnerabilities that only affect test dependencies that never run in production. The key is that each ignored finding needs documentation explaining why and a date when you'll re-evaluate. It becomes security theater when teams use it to sweep findings under the rug just to pass compliance checks, or when they ignore critical vulnerabilities permanently without ever reviewing them again. + +d) Having an SBOM solves the problem of knowing what you're actually running. When a new vulnerability like Log4Shell is disclosed, you can immediately search your SBOM to see if you're affected and what version you're using. Without an SBOM you'd have to manually inspect every container and dependency. It also helps with license compliance and supply chain transparency. When auditors ask what's in your software, you can show them the SBOM instead of guessing. + +Task 2 — OWASP ZAP + +ZAP findings from baseline scan: +1. X-Content-Type-Options Header Missing - FIX (implemented middleware) +2. Storable and Cacheable Content - ACCEPT (API responses can be cached, re-evaluate in 3 months) +3. ZAP is Out of Date - ACCEPT (using pinned version 2.16.0, re-evaluate in 3 months) +4. Insufficient Site Isolation Against Spectre - ACCEPT (internal API, risk acceptable) + +Fix implemented: Security headers middleware in app/middleware/security.go + +Before fix - curl -I http://172.17.0.1:8080/health returned: +Content-Type: application/json + +After fix - curl -I http://172.17.0.1:8080/health returns: +Content-Security-Policy: default-src 'none'; frame-ancestors 'none' +Content-Type: application/json +Referrer-Policy: strict-origin-when-cross-origin +X-Content-Type-Options: nosniff +X-Frame-Options: DENY +X-Xss-Protection: 1; mode=block + +Unit test added in middleware/security_test.go that asserts headers are present. Test passes. + +ZAP re-scan confirmed X-Content-Type-Options Header Missing is now PASS (no longer WARN). + +Design questions: + +e) Middleware is the right approach because it applies headers to every route automatically. If I set headers in each handler individually, I might forget to do it for a new route or change one handler but not another. Middleware gives me a single place to add or modify headers. It also keeps security concerns separate from business logic - handlers focus on what they're supposed to do, not on HTTP header details. + +f) Content-Security-Policy: default-src 'none' means nothing can load from anywhere - no scripts, no styles, no images, no fonts. For a website this breaks everything because browsers need to load JavaScript and CSS to render pages. For an API like QuickNotes that only returns JSON, there's nothing to load so it's perfectly fine. The policy protects against injection attacks without breaking functionality because the API doesn't serve HTML content anyway. + +g) The problem with accepting all findings without review is that you might miss something important. Some informational findings could indicate real issues or become exploitable in combination with other problems. You also lose the opportunity to learn what the scanner is telling you and how to improve your security. Accumulating ignored findings creates technical debt and makes it harder to notice when something genuinely new and serious appears. The review process helps you understand the security posture of your application. + +Bonus — govulncheck CI + +CI job added to .github/workflows/ci.yml: +- Runs govulncheck ./... against app/ +- Uses pinned version v1.0.0 +- Blocks PR if vulnerabilities found + +Demonstration: temporarily added vulnerable dependency, CI turned red. After revert, CI green. + +Design questions: + +h) The difference between reachability analysis and just listing CVEs is that reachability tells you whether the vulnerable code path is actually used in your application. This dramatically reduces false positives and triage workload. Instead of investigating every vulnerability in your dependency tree, you only need to look at the ones that could actually affect your running application. It saves time and helps prioritize real risks. + +i) Pinning govulncheck version matters because scanners change over time - new versions might have different detection rules or output formats. If you use @latest and the scanner updates, your CI could start failing or passing differently without any code change, which makes it hard to debug. A pinned version gives you reproducible builds and stable expectations. If you want to upgrade the scanner, you do it intentionally and can verify the results before committing. + +j) govulncheck only looks at Go code and its dependencies. Trivy scans the whole container image including OS packages like libc or openssl, any other language dependencies like Python packages in a virtualenv, configuration files for misconfigurations, Dockerfile issues, and even secrets like hardcoded credentials. Trivy gives you the full picture of what's in your container, while govulncheck focuses on a specific part of it. +