From the SonarQube security-hotspot review (2026-07-17). After clearing all code hotspots (90 reviewed SAFE) and the recursive-COPY hotspots (29 — mitigated by .dockerignore, verified), these remaining docker hotspots are REAL hardening gaps (not false positives), deferred to a dedicated pre-production container-hardening pass rather than churned blind during active development:
S6471 — runs as root (27 Dockerfiles): the final image has no USER directive, so containers run as root. Real defense-in-depth gap. NOT a drive-by fix: several services legitimately need care — e.g. NodeAgent runs nsenter for host commands, some bind low ports, some write to mounted volumes — so adding 'USER nonroot' must be done and TESTED per service (volume perms, port binds, privileged ops), not bulk-applied.
S6506 — HTTP client redirects not disabled (3 docker + 1 shell): curl/wget follow redirects; add --max-redirs 0 / -fsSL discipline where fetching from fixed URLs.
Recommend a single 'container hardening' PR per service (or a batch with a matrix test) before production. Dev-environment-acceptable today (same posture as #633 secret rotation). Left as status=TO_REVIEW in SonarQube deliberately — these are acknowledged real items, not cleared.
From the SonarQube security-hotspot review (2026-07-17). After clearing all code hotspots (90 reviewed SAFE) and the recursive-COPY hotspots (29 — mitigated by .dockerignore, verified), these remaining docker hotspots are REAL hardening gaps (not false positives), deferred to a dedicated pre-production container-hardening pass rather than churned blind during active development:
- **S6471 — runs as root (27 Dockerfiles):** the final image has no USER directive, so containers run as root. Real defense-in-depth gap. NOT a drive-by fix: several services legitimately need care — e.g. NodeAgent runs nsenter for host commands, some bind low ports, some write to mounted volumes — so adding 'USER nonroot' must be done and TESTED per service (volume perms, port binds, privileged ops), not bulk-applied.
- **S6500 — apt installs recommended packages (4):** add --no-install-recommends to shrink image / attack surface.
- **S6506 — HTTP client redirects not disabled (3 docker + 1 shell):** curl/wget follow redirects; add --max-redirs 0 / -fsSL discipline where fetching from fixed URLs.
Recommend a single 'container hardening' PR per service (or a batch with a matrix test) before production. Dev-environment-acceptable today (same posture as #633 secret rotation). Left as status=TO_REVIEW in SonarQube deliberately — these are acknowledged real items, not cleared.
Current state on origin/master (partial — leaving open):
Non-root USER (S6471): 19 of 32 Dockerfiles now declare a USER directive; 13 still have none and their final images run as root. Those 13 are the remaining work for this part.
--no-install-recommends (S6500): present in some (e.g. SpikerSoft.Api, SpikerSoft.AI.MCPServer, SpikerSoft.EventHandlers.ArtPipeProcessor) but not audited across all apt-using images.
curl/wget redirect hardening (S6506): not evidently applied.
So the non-root migration is well underway but not complete, and the S6500/S6506 legs are unfinished. Not closeable yet. (Note: no check-dockerfile-nonroot guard script exists on master, despite an earlier assumption one had landed — a CI guard would prevent regressions once the 13 stragglers are fixed.)
Current state on `origin/master` (partial — leaving open):
- **Non-root USER (S6471):** 19 of 32 Dockerfiles now declare a `USER` directive; **13 still have none** and their final images run as root. Those 13 are the remaining work for this part.
- **`--no-install-recommends` (S6500):** present in some (e.g. `SpikerSoft.Api`, `SpikerSoft.AI.MCPServer`, `SpikerSoft.EventHandlers.ArtPipeProcessor`) but not audited across all apt-using images.
- **curl/wget redirect hardening (S6506):** not evidently applied.
So the non-root migration is well underway but not complete, and the S6500/S6506 legs are unfinished. Not closeable yet. (Note: no `check-dockerfile-nonroot` guard script exists on master, despite an earlier assumption one had landed — a CI guard would prevent regressions once the 13 stragglers are fixed.)
Audited against origin/master — PARTIAL, and substantially further along than the ticket suggests. Concrete numbers so the remainder is a checklist rather than a sweep.
Non-root USER directive: 20 of 34 Dockerfiles. The 14 still running as root:
1. Dockerfile.old is in the list and shouldn't be.SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old is dead weight — it should be deleted rather than hardened, and it's inflating the S6471 count. Worth checking whether the SonarQube finding total includes it.
2. The regression guard from the earlier api-side work does not exist here. My notes recorded a check-dockerfile-nonroot.sh guard landing with the api.spikersoft.com hotspot review (PR #433). Searching this repo for nonroot/non-root returns nothing — so whatever guard exists isn't in spikersoft-backend, and nothing prevents the next new Dockerfile from arriving as root. Given 20 of 34 are already done, adding the guard now is what makes that progress durable; without it this ticket will partially regress.
Suggested split: the 14 are not equivalent. GameServer, GameServer.Initializer and NxCacheServer are .NET services where a USER line is near-trivial. The two Python ones (ImageDescription, Trellis3D) and the GPU-adjacent workers may need care around device access and volume ownership, so they're a separate slice. Doing the easy eleven plus the guard would take this from 20/34 to 31/34 with the ratchet in place.
The curl/wget redirect item (S6506) I did not check — flagging that as unverified rather than implying it's covered.
Audited against `origin/master` — **PARTIAL, and substantially further along than the ticket suggests.** Concrete numbers so the remainder is a checklist rather than a sweep.
**Non-root `USER` directive: 20 of 34 Dockerfiles.** The 14 still running as root:
```
SpikerSoft.EventHandlers.BlogMediaProcessor/Dockerfile
SpikerSoft.EventHandlers.BookManagement/Dockerfile
SpikerSoft.EventHandlers.FileMovement/Dockerfile
SpikerSoft.EventHandlers.GameEvents/Dockerfile
SpikerSoft.EventHandlers.GpuCoordinator/Dockerfile
SpikerSoft.EventHandlers.ImageDescription.Python/Dockerfile
SpikerSoft.EventHandlers.LessonVideoProcessor/Dockerfile
SpikerSoft.EventHandlers.MetadataExtractor/Dockerfile
SpikerSoft.EventHandlers.PhotographProcessor/Dockerfile
SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile
SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old
SpikerSoft.GameServer.Initializer/Dockerfile
SpikerSoft.GameServer/Dockerfile
SpikerSoft.NxCacheServer/Dockerfile
```
**`--no-install-recommends`: 19 of 34.**
Two observations worth acting on:
**1. `Dockerfile.old` is in the list and shouldn't be.** `SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old` is dead weight — it should be deleted rather than hardened, and it's inflating the S6471 count. Worth checking whether the SonarQube finding total includes it.
**2. The regression guard from the earlier api-side work does not exist here.** My notes recorded a `check-dockerfile-nonroot.sh` guard landing with the api.spikersoft.com hotspot review (PR #433). Searching this repo for `nonroot`/`non-root` returns **nothing** — so whatever guard exists isn't in spikersoft-backend, and nothing prevents the next new Dockerfile from arriving as root. Given 20 of 34 are already done, adding the guard now is what makes that progress durable; without it this ticket will partially regress.
**Suggested split:** the 14 are not equivalent. `GameServer`, `GameServer.Initializer` and `NxCacheServer` are .NET services where a `USER` line is near-trivial. The two Python ones (`ImageDescription`, `Trellis3D`) and the GPU-adjacent workers may need care around device access and volume ownership, so they're a separate slice. Doing the easy eleven plus the guard would take this from 20/34 to 31/34 with the ratchet in place.
The curl/wget redirect item (S6506) I did not check — flagging that as unverified rather than implying it's covered.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
From the SonarQube security-hotspot review (2026-07-17). After clearing all code hotspots (90 reviewed SAFE) and the recursive-COPY hotspots (29 — mitigated by .dockerignore, verified), these remaining docker hotspots are REAL hardening gaps (not false positives), deferred to a dedicated pre-production container-hardening pass rather than churned blind during active development:
Recommend a single 'container hardening' PR per service (or a batch with a matrix test) before production. Dev-environment-acceptable today (same posture as #633 secret rotation). Left as status=TO_REVIEW in SonarQube deliberately — these are acknowledged real items, not cleared.
Current state on
origin/master(partial — leaving open):USERdirective; 13 still have none and their final images run as root. Those 13 are the remaining work for this part.--no-install-recommends(S6500): present in some (e.g.SpikerSoft.Api,SpikerSoft.AI.MCPServer,SpikerSoft.EventHandlers.ArtPipeProcessor) but not audited across all apt-using images.So the non-root migration is well underway but not complete, and the S6500/S6506 legs are unfinished. Not closeable yet. (Note: no
check-dockerfile-nonrootguard script exists on master, despite an earlier assumption one had landed — a CI guard would prevent regressions once the 13 stragglers are fixed.)Audited against
origin/master— PARTIAL, and substantially further along than the ticket suggests. Concrete numbers so the remainder is a checklist rather than a sweep.Non-root
USERdirective: 20 of 34 Dockerfiles. The 14 still running as root:--no-install-recommends: 19 of 34.Two observations worth acting on:
1.
Dockerfile.oldis in the list and shouldn't be.SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.oldis dead weight — it should be deleted rather than hardened, and it's inflating the S6471 count. Worth checking whether the SonarQube finding total includes it.2. The regression guard from the earlier api-side work does not exist here. My notes recorded a
check-dockerfile-nonroot.shguard landing with the api.spikersoft.com hotspot review (PR #433). Searching this repo fornonroot/non-rootreturns nothing — so whatever guard exists isn't in spikersoft-backend, and nothing prevents the next new Dockerfile from arriving as root. Given 20 of 34 are already done, adding the guard now is what makes that progress durable; without it this ticket will partially regress.Suggested split: the 14 are not equivalent.
GameServer,GameServer.InitializerandNxCacheServerare .NET services where aUSERline is near-trivial. The two Python ones (ImageDescription,Trellis3D) and the GPU-adjacent workers may need care around device access and volume ownership, so they're a separate slice. Doing the easy eleven plus the guard would take this from 20/34 to 31/34 with the ratchet in place.The curl/wget redirect item (S6506) I did not check — flagging that as unverified rather than implying it's covered.