Initial import of demo-secure

This commit is contained in:
gitea-admin 2026-05-20 06:10:29 +00:00
commit 352d79e5fc
7 changed files with 180 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
demo-secure

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM registry.access.redhat.com/ubi9/go-toolset:1.22 AS build
WORKDIR /opt/app-root/src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ARG GIT_COMMIT=unknown
ENV CGO_ENABLED=0
RUN go build -trimpath -ldflags "-s -w -X main.commit=${GIT_COMMIT}" -o /tmp/demo-secure .
FROM registry.access.redhat.com/ubi9-minimal:latest
LABEL org.opencontainers.image.title="demo-secure" \
org.opencontainers.image.description="RHADS supply-chain demo service" \
org.opencontainers.image.source="http://nas1.internal.hilltopcampground.net:23232/openshift-lab/bootstrap"
COPY --from=build /tmp/demo-secure /usr/local/bin/demo-secure
USER 1001
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/demo-secure"]

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# demo-secure
Minimal Go HTTP service used by the RHADS supply-chain demo.
## The deliberate CVE
`go.mod` pins `github.com/dgrijalva/jwt-go v3.2.0+incompatible`, which has a
documented vulnerability (CVE-2020-26160 — `alg=none` not rejected). The
`/verify` handler actually calls into this library, so SBOM scanners pick the
dependency up as in-use and RHTPA flags it.
## Fixing the CVE (the "patch" act)
1. `go get github.com/golang-jwt/jwt/v5`
2. `go mod tidy`
3. Update the import in `main.go` from `github.com/dgrijalva/jwt-go` to
`github.com/golang-jwt/jwt/v5`
4. Commit and push — the Tekton pipeline rebuilds, re-signs, ArgoCD redeploys,
RHTPA dashboard flips from red to green.
## Endpoints
- `GET /` — landing page
- `GET /version` — JSON with Go version + dep list (proves which JWT lib is shipped)
- `GET /healthz` — liveness probe target
- `POST /verify``Authorization: Bearer <jwt>` → returns claims if valid

27
catalog-info.yaml Normal file
View File

@ -0,0 +1,27 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: demo-secure
description: Minimal Go service demonstrating the RHADS supply chain (build → sign → scan → patch).
annotations:
backstage.io/kubernetes-id: demo-secure
backstage.io/kubernetes-namespace: demo-secure
backstage.io/techdocs-ref: dir:.
janus-idp.io/tekton: demo-secure
argocd/app-name: demo-secure
links:
- url: https://demo-secure-demo-secure.apps.lab.hibachi.ninja
title: Live service
spec:
type: service
lifecycle: experimental
owner: platform
system: rhads-demo
---
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: rhads-demo
description: End-to-end RHADS supply-chain demo (RHDH + RHTAS + RHTPA).
spec:
owner: platform

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/openshift-lab/demo-secure
go 1.22
require github.com/dgrijalva/jwt-go v3.2.0+incompatible

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=

102
main.go Normal file
View File

@ -0,0 +1,102 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"runtime/debug"
jwt "github.com/dgrijalva/jwt-go"
)
func signingKey() []byte {
if k := os.Getenv("JWT_KEY"); k != "" {
return []byte(k)
}
return []byte("demo-only-not-for-real-use")
}
type versionInfo struct {
Service string `json:"service"`
Commit string `json:"commit"`
GoVersion string `json:"go_version"`
Deps map[string]string `json:"deps"`
JWTLibrary string `json:"jwt_library"`
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", index)
mux.HandleFunc("/version", version)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "ok")
})
mux.HandleFunc("/verify", verifyToken)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("demo-secure listening on :%s", port)
if err := http.ListenAndServe(":"+port, mux); err != nil {
log.Fatal(err)
}
}
func index(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, `<!doctype html>
<html><head><title>demo-secure</title></head>
<body style="font-family:sans-serif;max-width:40em;margin:3em auto">
<h1>demo-secure</h1>
<p>A minimal Go service used to demonstrate the RHADS supply chain:
RHDH onboarding &rarr; RHTAS signing &rarr; RHTPA scanning &rarr; patch.</p>
<ul>
<li><a href="/version">/version</a> &mdash; dependency info</li>
<li><a href="/healthz">/healthz</a> &mdash; liveness</li>
<li><code>POST /verify</code> with a Bearer token &mdash; exercises the JWT library</li>
</ul>
</body></html>`)
}
func version(w http.ResponseWriter, _ *http.Request) {
info := versionInfo{
Service: "demo-secure",
Commit: os.Getenv("GIT_COMMIT"),
Deps: map[string]string{},
}
if bi, ok := debug.ReadBuildInfo(); ok {
info.GoVersion = bi.GoVersion
for _, d := range bi.Deps {
info.Deps[d.Path] = d.Version
if d.Path == "github.com/dgrijalva/jwt-go" || d.Path == "github.com/golang-jwt/jwt/v5" {
info.JWTLibrary = fmt.Sprintf("%s@%s", d.Path, d.Version)
}
}
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(info)
}
func verifyToken(w http.ResponseWriter, r *http.Request) {
tok := r.Header.Get("Authorization")
if len(tok) > 7 && tok[:7] == "Bearer " {
tok = tok[7:]
}
if tok == "" {
http.Error(w, "missing bearer token", http.StatusUnauthorized)
return
}
parsed, err := jwt.Parse(tok, func(t *jwt.Token) (interface{}, error) {
return signingKey(), nil
})
if err != nil || !parsed.Valid {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(parsed.Claims)
}