Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/.hadolint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ failure-threshold: warning
trustedRegistries:
- ghcr.io
- docker.io
# Distroless runtime-образы публикуются только здесь.
- gcr.io

# Отключаем правила, которые либо неприменимы, либо слишком душные.
ignored:
Expand Down
47 changes: 34 additions & 13 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -306,19 +306,27 @@ jobs:
cache-from: type=gha,scope=${{ matrix.component }}
cache-to: type=gha,mode=max,scope=${{ matrix.component }}

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: red-lycoris-${{ matrix.component }}:pr-scan
format: sarif
output: trivy-${{ matrix.component }}.sarif
severity: CRITICAL,HIGH
exit-code: '1'
# Не падаем на CVE без фикса — это шум, исправить мы их не можем.
ignore-unfixed: true
# Сканируем только OS-пакеты и зависимости приложения.
scanners: vuln,secret
trivy-args: "--skip-version-check"
# Ставим «голый» Trivy фиксированной версии вместо trivy-action —
# меньше магии, полный контроль над флагами, предсказуемая версия.
- name: Install Trivy
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
| sh -s -- -b /usr/local/bin v0.71.2
trivy --version

# Шаг 1 — отчёт. Всегда exit 0, чтобы SARIF гарантированно сгенерировался
# и загрузился в Security tab, даже когда найдены уязвимости.
- name: Generate Trivy SARIF report
run: |
trivy image \
--format sarif \
--output trivy-${{ matrix.component }}.sarif \
--severity CRITICAL,HIGH \
--ignore-unfixed \
--scanners vuln,secret \
--skip-version-check \
--exit-code 0 \
red-lycoris-${{ matrix.component }}:pr-scan

- name: Upload Trivy SARIF to GitHub Security
if: always()
Expand All @@ -327,6 +335,19 @@ jobs:
sarif_file: trivy-${{ matrix.component }}.sarif
category: trivy-${{ matrix.component }}

# Шаг 2 — гейт. Отдельный прогон валит билд на CRITICAL/HIGH с фиксом.
# БД уже скачана на шаге 1, поэтому повтор почти бесплатный.
- name: Fail on CRITICAL/HIGH vulnerabilities
run: |
trivy image \
--format table \
--severity CRITICAL,HIGH \
--ignore-unfixed \
--scanners vuln,secret \
--skip-version-check \
--exit-code 1 \
red-lycoris-${{ matrix.component }}:pr-scan

# ────────────────────────────────────────────────────────────────────
# FINAL: aggregator, на который удобно вешать branch protection.
# ────────────────────────────────────────────────────────────────────
Expand Down
35 changes: 6 additions & 29 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ── Stage 1: Build ──
FROM golang:1.25-bookworm AS builder
FROM golang:1.25.11-bookworm AS builder

WORKDIR /build

Expand All @@ -12,40 +12,17 @@ ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_DATE=unknown

# Один RUN на все бинарники: server с version-метаданными, admin и
# healthcheck (последний нужен distroless-образу без shell и curl).
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-ldflags="-s -w \
-X redlycoris/internal/version.Version=${VERSION} \
-X redlycoris/internal/version.Commit=${COMMIT} \
-X redlycoris/internal/version.Date=${BUILD_DATE}" \
-o /build/redlycoris ./cmd/server
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o /build/admin ./cmd/admin

# Build a tiny healthcheck binary for distroless runtime images.
RUN cat > /tmp/healthcheck.go <<'EOF'
package main

import (
"net/http"
"os"
"time"
)

func main() {
client := &http.Client{Timeout: 2 * time.Second}
resp, err := client.Get("http://127.0.0.1:8080/healthz")
if err != nil {
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
os.Exit(1)
}
}
EOF
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o /build/healthcheck /tmp/healthcheck.go
-o /build/redlycoris ./cmd/server \
&& go build -ldflags="-s -w" -o /build/admin ./cmd/admin \
&& go build -ldflags="-s -w" -o /build/healthcheck ./cmd/healthcheck

# ── Stage 2: Run ──
# Distroless runtime avoids package-manager RUN steps and keeps the image minimal.
Expand Down
21 changes: 21 additions & 0 deletions backend/cmd/healthcheck/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Command healthcheck is a tiny self-contained probe used by the distroless
// runtime image, where no shell or curl is available for HEALTHCHECK.
package main

import (
"net/http"
"os"
"time"
)

func main() {
client := &http.Client{Timeout: 2 * time.Second}
resp, err := client.Get("http://127.0.0.1:8080/healthz")
if err != nil {
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
os.Exit(1)
}
}
12 changes: 6 additions & 6 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ go 1.25.0

require (
github.com/alexedwards/argon2id v1.0.0
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/chi/v5 v5.2.4
github.com/golang-migrate/migrate/v4 v4.17.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.9.2
github.com/microcosm-cc/bluemonday v1.0.27
github.com/redis/go-redis/v9 v9.7.3
github.com/swaggest/swgui v1.8.5
github.com/xuri/excelize/v2 v2.8.1
golang.org/x/net v0.50.0
golang.org/x/sync v0.19.0
golang.org/x/text v0.34.0
golang.org/x/net v0.55.0
golang.org/x/sync v0.21.0
golang.org/x/text v0.38.0
)

require (
Expand All @@ -35,6 +35,6 @@ require (
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/sys v0.45.0 // indirect
)
30 changes: 16 additions & 14 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
Expand Down Expand Up @@ -99,26 +101,26 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -127,8 +129,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
Expand All @@ -140,14 +142,14 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
5 changes: 1 addition & 4 deletions backend/internal/api/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,7 @@ func (s *optionalProjectSource) UnmarshalJSON(raw []byte) error {
s.Null = true
return nil
}
if err := json.Unmarshal(raw, &s.Value); err != nil {
return err
}
return nil
return json.Unmarshal(raw, &s.Value)
}

type nullableIntField struct {
Expand Down
10 changes: 5 additions & 5 deletions backend/internal/parser/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@ func (f *flexCWEIDs) UnmarshalJSON(data []byte) error {
return nil
}

func clampInt(n, min, max int) int {
if n < min {
return min
func clampInt(n, lo, hi int) int {
if n < lo {
return lo
}
if n > max {
return max
if n > hi {
return hi
}
return n
}
Expand Down
2 changes: 1 addition & 1 deletion backend/internal/parser/generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func TestFlexCWEIDs(t *testing.T) {
continue
}
for i := range ids {
if int(ids[i]) != tc.want[i] {
if ids[i] != tc.want[i] {
t.Errorf("input %s [%d]: got %d, want %d", tc.input, i, ids[i], tc.want[i])
}
}
Expand Down
50 changes: 2 additions & 48 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,54 +40,8 @@ RUN mkdir -p /tmp/nginx/client_temp /tmp/nginx/proxy_temp /tmp/nginx/fastcgi_tem
/var/run /var/log/nginx \
&& chown -R nginx:nginx /tmp/nginx /var/cache/nginx /var/run /var/log/nginx /usr/share/nginx/html

RUN cat > /etc/nginx/nginx.conf <<'NGINXCONF'
pid /tmp/nginx/nginx.pid;
worker_processes auto;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

access_log /dev/stdout;
error_log /dev/stderr warn;

sendfile on;
keepalive_timeout 65;

client_body_temp_path /tmp/nginx/client_temp;
proxy_temp_path /tmp/nginx/proxy_temp;
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
scgi_temp_path /tmp/nginx/scgi_temp;

include /etc/nginx/conf.d/*.conf;
}
NGINXCONF

RUN cat > /etc/nginx/conf.d/default.conf <<'SERVERCONF'
server {
listen 3000;
root /usr/share/nginx/html;
index index.html;

resolver 127.0.0.11 ipv6=off;
set $backend_upstream backend:8080;

location /api/ {
proxy_pass http://$backend_upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

location / {
try_files $uri $uri/ /index.html;
}
}
SERVERCONF
COPY nginx/nginx.conf /etc/nginx/nginx.conf
COPY nginx/default.conf /etc/nginx/conf.d/default.conf

USER nginx

Expand Down
18 changes: 18 additions & 0 deletions frontend/nginx/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
server {
listen 3000;
root /usr/share/nginx/html;
index index.html;

resolver 127.0.0.11 ipv6=off;
set $backend_upstream backend:8080;

location /api/ {
proxy_pass http://$backend_upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

location / {
try_files $uri $uri/ /index.html;
}
}
25 changes: 25 additions & 0 deletions frontend/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pid /tmp/nginx/nginx.pid;
worker_processes auto;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

access_log /dev/stdout;
error_log /dev/stderr warn;

sendfile on;
keepalive_timeout 65;

client_body_temp_path /tmp/nginx/client_temp;
proxy_temp_path /tmp/nginx/proxy_temp;
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
scgi_temp_path /tmp/nginx/scgi_temp;

include /etc/nginx/conf.d/*.conf;
}
Loading