Skip to content

Issue #245978 feat: Provider BE - Docker Security Hotspot fixes#131

Open
rajnishdargan wants to merge 9 commits intoPSMRI:mainfrom
rajnishdargan:security-fixes
Open

Issue #245978 feat: Provider BE - Docker Security Hotspot fixes#131
rajnishdargan wants to merge 9 commits intoPSMRI:mainfrom
rajnishdargan:security-fixes

Conversation

@rajnishdargan
Copy link
Contributor

@rajnishdargan rajnishdargan commented Aug 29, 2025

PR Type

Enhancement


Description

  • Docker security hardening with non-root user

  • Added comprehensive dockerignore file

  • Updated service message string


Diagram Walkthrough

flowchart LR
  A["Dockerfile"] --> B["Non-root user setup"]
  A --> C["Multi-stage build optimization"]
  D[".dockerignore"] --> E["File exclusion rules"]
  F["app.service.ts"] --> G["Updated service message"]
Loading

File Walkthrough

Relevant files
Enhancement
Dockerfile
Docker security hardening with non-root user                         

Dockerfile

  • Added non-root user appuser with specific UID/GID 1001
  • Implemented proper file ownership with --chown flags
  • Switched to Alpine base image for smaller footprint
  • Changed runtime command to production mode
+24/-17 
Configuration changes
.dockerignore
Comprehensive dockerignore file creation                                 

.dockerignore

  • Added comprehensive file exclusion patterns
  • Excluded sensitive files like environment variables
  • Excluded development artifacts and temporary files
  • Added security-focused exclusions for uploads directory
+81/-0   
Miscellaneous
app.service.ts
Updated service greeting message                                                 

src/app.service.ts

  • Changed return message from 'Hello World!' to 'Strapi Provider MW
    Backend!!'
+1/-1     

@coderabbitai
Copy link

coderabbitai bot commented Aug 29, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • App now defaults to port 7000 when PORT is unset/invalid and listens on all interfaces (0.0.0.0).
    • Container runs as a non-root user and exposes port 7000.
  • Chores

    • Added a .dockerignore to reduce Docker build context.
    • Reworked Docker build to a lean multi-stage image with production-only dependencies and NODE_ENV=production.
  • Style

    • Updated the default greeting message displayed by the app.
  • Tests

    • Adjusted unit and end-to-end tests to match the new greeting.

Walkthrough

Added a .dockerignore; replaced Dockerfile with a multi-stage builder+runner using node:20-alpine and non-root runtime exposing port 7000; changed root greeting to "Strapi Provider MW Backend!!"; updated unit and e2e tests; default bootstrap port is now 7000 and app listens on 0.0.0.0.

Changes

Cohort / File(s) Summary of Changes
Docker ignore rules
\.dockerignore
Added comprehensive Docker ignore patterns to minimize build context (node_modules, lockfiles/logs, env files, VCS metadata, docs/tests/coverage, IDE caches, OS artifacts, build outputs, temp folders, keys/uploads, Docker files).
Multi-stage Docker image
Dockerfile
Replaced single-stage image with multi-stage build (FROM node:20-alpine AS builder and AS runner); installs build deps in builder, runs npm ci, npx prisma generate, and npm run build; copies selective runtime artifacts (prisma/, dist/, Prisma client files) into runner; creates non-root appuser/appgroup, sets ownership/permissions, installs runtime deps with npm ci --omit=dev --ignore-scripts, exposes port 7000, and sets CMD ["node","dist/main.js"].
Greeting & tests
src/app.service.ts, src/app.controller.spec.ts, test/app.e2e-spec.ts
Root greeting changed from "Hello World!" to "Strapi Provider MW Backend!!"; unit and e2e tests updated to assert the new message.
Bootstrap / port binding
src/main.ts
Removed PORT console log; parses process.env.PORT as a number and defaults to 7000 if unset/invalid; server listens on 0.0.0.0 with the computed port.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev
  participant Docker as Docker build
  participant Builder as Builder (node:20-alpine)
  participant Runner as Runner (node:20-alpine)
  participant Container as App Container

  Dev->>Docker: docker build .
  Docker->>Builder: execute build stage
  note right of Builder #e8f5e9: npm ci (builder)\nnpx prisma generate\nnpm run build\nproduce dist/ and prisma/
  Builder-->>Docker: build artifacts available
  Docker->>Runner: copy runtime artifacts (dist/, prisma/, node_modules/.prisma/, node_modules/@prisma/client/)
  note right of Runner #fff3e0: install runtime deps (npm ci --omit=dev)\ncreate appuser/appgroup\nset ownership & perms\nEXPOSE 7000
  Runner-->>Docker: final image produced
  Dev->>Container: docker run -p 7000:7000 image
  Container->>Container: start as non-root user -> node dist/main.js\nlisten on 0.0.0.0:7000
  Container-->>Dev: service available on port 7000
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between b92af66 and fa8b38e.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@qodo-code-review
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Build vs. user permissions

You copy files with --chown=appuser:appgroup and later switch to USER appuser, but some build steps (npm ci, prisma generate, npm run build) may require root or write access to global cache/dirs. Validate that build succeeds in CI and that Prisma generation/write paths and TypeScript build outputs are writable under current ownership and user model.

# Install all dependencies as root first (including dev dependencies for Prisma)
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci && npm cache clean --force

# Copy application files with proper ownership
COPY --chown=appuser:appgroup prisma/ ./prisma/
COPY --chown=appuser:appgroup src/ ./src/
COPY --chown=appuser:appgroup nest-cli.json ./
COPY --chown=appuser:appgroup tsconfig*.json ./
COPY --chown=appuser:appgroup eslint.config.mjs ./

# Optional check to confirm schema exists, generate Prisma client and build project as root
RUN ls -la prisma/schema.prisma && \
    npx prisma generate && \
    npm run build
Excluding Dockerfile

Ignoring Dockerfile* in .dockerignore can break docker builds that rely on context-included Dockerfiles (e.g., docker build without -f). Confirm your build pipeline always references the Dockerfile by path outside context or remove this exclusion.

Dockerfile*
docker-compose*.yml
.dockerignore
Ignoring README/docs

Excluding README.md and docs may hinder image consumers who rely on docs copied into the image or during build steps. Verify no build step expects these files (e.g., versioning, metadata).

README.md
CHANGELOG.md
*.md
docs/

@qodo-code-review
Copy link

qodo-code-review bot commented Aug 29, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Use proper multi-stage build

The Dockerfile claims multi-stage optimization but builds and runs in a single
stage, shipping dev dependencies and source files into production. Implement a
true multi-stage build (builder + runtime) to install and build with dev deps,
then copy only the pruned production node_modules, Prisma client, and dist into
a minimal runtime image under a non-root user. This will significantly reduce
image size and attack surface while aligning with the PR’s stated goal.

Examples:

Dockerfile [1-34]
FROM node:20-alpine AS dependencies

# Create app group and user with specific UID/GID
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

# Create and set ownership of app directory
RUN mkdir -p /app && chown -R appuser:appgroup /app

WORKDIR /app

 ... (clipped 24 lines)

Solution Walkthrough:

Before:

# Dockerfile (single stage)
FROM node:20-alpine AS dependencies

# Create user, copy files, install ALL dependencies
RUN addgroup ... && adduser ...
WORKDIR /app
COPY package*.json ./
RUN npm ci

# Copy source code into the final image
COPY src/ ./src/
COPY prisma/ ./prisma/
...

# Build project in the same stage
RUN npx prisma generate && npm run build

# Run as non-root user
USER appuser
CMD ["npm", "run", "start:prod"]

After:

# Dockerfile (multi-stage)

# --- Builder Stage ---
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx prisma generate && npm run build

# --- Runtime Stage ---
FROM node:20-alpine
RUN addgroup ... && adduser ...
WORKDIR /app

# Copy only production artifacts from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules # Assumes pruning dev deps
COPY --from=builder /app/package*.json ./

USER appuser
CMD ["npm", "run", "start:prod"]
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical flaw where the Dockerfile fails to implement a multi-stage build, contradicting a stated goal of the PR and resulting in a bloated, less secure image.

High
Possible issue
Target Prisma for Alpine

On Alpine (musl), Prisma often fails at runtime with a "platform not found"
engine error. Generate the Prisma Client for linux-musl explicitly to avoid
binary incompatibility. Adjust the generate step to target the correct platform.

Dockerfile [24-26]

 RUN ls -la prisma/schema.prisma && \
-    npx prisma generate && \
+    npx prisma generate --binary-targets=linux-musl && \
     npm run build
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical compatibility issue with Prisma on Alpine Linux, which was introduced in the PR. Applying this fix prevents a likely runtime failure, making it highly important.

High
Run Node directly at runtime
Suggestion Impact:The Dockerfile CMD was updated to execute Node directly: it changed from ["npm", "run", "start:prod"] to ["node", "dist/main.js"].

code diff:

-CMD ["npm", "run", "start:prod"]
+CMD ["node", "dist/main.js"]

Avoid running npm as the entrypoint under a non-root user, as it may require a
writable HOME/cache and can fail with permission errors. Execute Node directly
to run the built app.

Dockerfile [32-34]

 EXPOSE 7000
 
-CMD ["npm", "run", "start:prod"]
+CMD ["node", "dist/main.js"]

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: This is a valid best practice for production Docker images, as it avoids potential npm permission issues and overhead. It improves the container's robustness and follows recommended patterns.

Medium
General
Create writable uploads directory

Since uploads/ is excluded from the build context, ensure the directory exists
and is writable at runtime. Create it during build and set ownership to the
non-root user to prevent upload/write failures.

Dockerfile [17-21]

 COPY --chown=appuser:appgroup prisma/ ./prisma/
 COPY --chown=appuser:appgroup src/ ./src/
 COPY --chown=appuser:appgroup nest-cli.json ./
 COPY --chown=appuser:appgroup tsconfig*.json ./
 COPY --chown=appuser:appgroup eslint.config.mjs ./
 
+# Ensure runtime-writable directories exist
+RUN mkdir -p /app/uploads && chown -R appuser:appgroup /app/uploads
+
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the uploads/ directory is excluded from the build context via .dockerignore but is likely needed at runtime. Creating it and setting permissions prevents potential file upload failures.

Medium
  • Update

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Dockerfile (1)

1-35: Use a multi-stage build to drop dev deps and avoid shipping source

Current image keeps devDependencies and src, increasing attack surface and size. Switch to multi-stage, prune dev deps, and run as non-root in the final stage.

Apply:

-FROM node:20-alpine AS dependencies
-# Create app group and user with specific UID/GID
-RUN addgroup -g 1001 -S appgroup && \
-    adduser -S appuser -u 1001 -G appgroup
-# Create and set ownership of app directory
-RUN mkdir -p /app && chown -R appuser:appgroup /app
-WORKDIR /app
-# Install all dependencies as root first (including dev dependencies for Prisma)
-COPY --chown=appuser:appgroup package*.json ./
-RUN npm ci && npm cache clean --force
-# Copy application files with proper ownership
-COPY --chown=appuser:appgroup prisma/ ./prisma/
-COPY --chown=appuser:appgroup src/ ./src/
-COPY --chown=appuser:appgroup nest-cli.json ./
-COPY --chown=appuser:appgroup tsconfig*.json ./
-COPY --chown=appuser:appgroup eslint.config.mjs ./
-# Optional check to confirm schema exists, generate Prisma client and build project as root
-RUN ls -la prisma/schema.prisma && \
-    npx prisma generate && \
-    npm run build
-# Switch to non-root user for runtime
-USER appuser
-# Expose the correct port for your application
-EXPOSE 7000
-CMD ["npm", "run", "start:prod"]
+FROM node:20-alpine AS builder
+WORKDIR /app
+# Install deps (dev included for build)
+COPY package*.json ./
+RUN npm ci --no-audit --no-fund && npm cache clean --force
+# Copy sources
+COPY prisma/ ./prisma/
+COPY tsconfig*.json ./
+COPY nest-cli.json ./
+COPY src/ ./src/
+# Generate Prisma client and build
+RUN test -f prisma/schema.prisma && npx prisma generate && npm run build
+# Prune devDependencies for runtime
+RUN npm prune --omit=dev
+
+FROM node:20-alpine AS runner
+ENV NODE_ENV=production
+WORKDIR /app
+# Create non-root user
+RUN addgroup -g 1001 -S appgroup && adduser -S -u 1001 -G appgroup -s /sbin/nologin -h /home/appuser appuser \
+ && mkdir -p /app && chown -R appuser:appgroup /app
+# Copy minimal runtime assets
+COPY --from=builder /app/package*.json ./
+COPY --from=builder /app/node_modules ./node_modules
+COPY --from=builder /app/dist ./dist
+USER appuser
+EXPOSE 7000
+# If start:prod calls `node dist/main.js`, keep as-is. Otherwise consider: CMD ["node","dist/main.js"]
+CMD ["npm", "run", "start:prod"]
🧹 Nitpick comments (2)
Dockerfile (2)

12-15: Supply-chain hardening for npm ci
Skip audit/fund noise; consider ignoring lifecycle scripts only if you keep an explicit prisma generate.

Apply:

-RUN npm ci && npm cache clean --force
+RUN npm ci --no-audit --no-fund && npm cache clean --force
+# If your deps don't rely on install scripts, you can add: --ignore-scripts
+# and keep the explicit `npx prisma generate` later.

31-35: Add a HEALTHCHECK (optional)
If you expose a health endpoint, wire it for container health to aid orchestrators.

Example:

 EXPOSE 7000
+HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
+ CMD node -e "require('http').get('http://127.0.0.1:7000/health', r => process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"
 CMD ["npm", "run", "start:prod"]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6995725 and c19f2b3.

📒 Files selected for processing (3)
  • .dockerignore (1 hunks)
  • Dockerfile (1 hunks)
  • src/app.service.ts (1 hunks)
🔇 Additional comments (1)
Dockerfile (1)

34-34: No changes required for Dockerfile CMD
The start:prod script is defined as node dist/main and doesn’t rely on dev-only tools, so leaving CMD ["npm", "run", "start:prod"] is appropriate.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
Dockerfile (1)

4-5: Use a non-login shell and set a home for the system user.

Harden the system account; avoid interactive shells and provide a writable HOME for npm logs/cache.

-RUN addgroup -g 1001 -S appgroup && \
-    adduser -S appuser -u 1001 -G appgroup
+RUN addgroup -g 1001 -S appgroup && \
+    adduser -S appuser -u 1001 -G appgroup -s /sbin/nologin -h /home/appuser

If /sbin/nologin is unavailable, use /bin/false.

🧹 Nitpick comments (4)
Dockerfile (4)

8-8: Avoid redundant chown; prefer COPY --chown to reduce layers.

You first chown /app to appuser, then later chown everything to root:appgroup. Drop the early chown and use --chown on COPY.

-RUN mkdir -p /app && chown -R appuser:appgroup /app
+RUN mkdir -p /app
@@
-COPY prisma/ ./prisma/
-COPY src/ ./src/
-COPY nest-cli.json ./
-COPY tsconfig*.json ./
-COPY eslint.config.mjs ./
+COPY --chown=root:appgroup prisma/ ./prisma/
+COPY --chown=root:appgroup src/ ./src/
+COPY --chown=root:appgroup nest-cli.json ./
+COPY --chown=root:appgroup tsconfig*.json ./
+COPY --chown=root:appgroup eslint.config.mjs ./

Also applies to: 24-34


25-25: Remove debug ls -la in build step.

Keeps layers clean and reproducible.

-RUN ls -la prisma/schema.prisma && \
+RUN \
     npx prisma generate && \
     npm run build && \

36-42: Prefer running node directly; set npm cache to writable dir if you keep npm.

npm run can write to $HOME/.npm; without a HOME it may warn/fail. Either run node directly (preferred) or set cache.

-USER appuser
-EXPOSE 7000
-CMD ["npm", "run", "start:prod"]
+ENV NPM_CONFIG_CACHE=/tmp/.npm
+USER appuser
+EXPOSE 7000
+# or: CMD ["node","dist/main.js"]
+CMD ["npm","run","start:prod"]

1-1: Alpine + Prisma: ensure required runtime libs.

On Alpine, Prisma engines may need libc6-compat (and often openssl) at runtime. Install in the final stage.

-FROM node:20-alpine AS dependencies
+FROM node:20-alpine AS dependencies
+RUN apk add --no-cache libc6-compat openssl

Also applies to: 24-27

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c19f2b3 and 97c6215.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/app.e2e-spec.ts (1)

10-17: Close the Nest app to avoid open-handle leaks; prefer beforeAll/afterAll.

Tests may hang or leak resources without app.close(). Initialize once, tear down once.

-  beforeEach(async () => {
+  beforeAll(async () => {
     const moduleFixture: TestingModule = await Test.createTestingModule({
       imports: [AppModule],
     }).compile();

     app = moduleFixture.createNestApplication();
     await app.init();
-  });
+  });
+
+  afterAll(async () => {
+    await app.close();
+  });
♻️ Duplicate comments (2)
src/app.controller.spec.ts (2)

18-19: Ensure consistency with e2e test and service return.

Same potential <br> mismatch risk as noted in e2e spec; verify the exact string once.


18-19: Optional: use a shared constant for the message.

Reference a single source (e.g., tests/utils/constants.ts) to avoid drift between unit and e2e tests.

🧹 Nitpick comments (2)
test/app.e2e-spec.ts (2)

8-8: Nit: Drop the supertest App generic.

INestApplication’s default generic is fine and avoids coupling to supertest types.

-  let app: INestApplication<App>;
+  let app: INestApplication;

23-23: De-duplicate the expected message across tests.

Consider a shared test constant (e.g., tests/utils/constants.ts) to keep expectations in sync.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 97c6215 and 0fb24fa.

📒 Files selected for processing (3)
  • Dockerfile (1 hunks)
  • src/app.controller.spec.ts (1 hunks)
  • test/app.e2e-spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile
🔇 Additional comments (3)
test/app.e2e-spec.ts (2)

23-23: Expectation string updated correctly.

Matches the new greeting.


23-23: AppService.getHello() return value matches test expectation
The service returns 'Strapi Provider MW Backend!!' (no <br>), so the test’s .expect('Strapi Provider MW Backend!!') is correct.

src/app.controller.spec.ts (1)

18-20: Unit test expectation updated correctly.

Aligned with the new greeting from AppService.getHello().

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
.dockerignore (1)

14-16: Harden ignore rules for package manager RC files (wildcards too)

Include wildcard variants to catch workspace/local overrides.

 .npmrc
 .yarnrc
 .pnpmrc
+*.npmrc
+*.yarnrc
+*.pnpmrc
Dockerfile (1)

1-41: Adopt a proper multi-stage build; ship only prod deps; set NODE_ENV; add init

Reduces attack surface and image size; keeps dev toolchain out of runtime.

-FROM node:20-alpine AS dependencies
-
-# Create app group and user with specific UID/GID, and set up app directory
-RUN addgroup -g 1001 -S appgroup && \
-    adduser -S appuser -u 1001 -G appgroup -s /sbin/nologin -h /home/appuser && \
-    mkdir -p /app && chown -R appuser:appgroup /app
-
-WORKDIR /app
-
-# Copy package files as root without giving write permissions to appuser
-COPY package*.json ./
-RUN chown root:appgroup package*.json && chmod 644 package*.json && \
-    npm ci --ignore-scripts && npm cache clean --force
-
-# Copy application files as root with read-only permissions
-COPY prisma/ ./prisma/
-COPY src/ ./src/
-COPY nest-cli.json ./
-COPY tsconfig*.json ./
-COPY eslint.config.mjs ./
-
-# Generate Prisma client and build project first (while files are still writable)
-RUN ls -la prisma/schema.prisma && \
-    npx prisma generate && \
-    npm run build && \
-    chown -R root:appgroup . && \
-    chmod -R o-rwx . && \
-    find . -type f -exec chmod g-w {} \; && \
-    find . -type d -exec chmod g-w {} \; && \
-    mkdir -p /app/logs /app/temp && \
-    chown appuser:appgroup /app/logs /app/temp && \
-    chmod 755 /app/logs /app/temp
-
-# Switch to non-root user for runtime
-USER appuser
-
-# Expose the correct port for your application
-EXPOSE 7000
-
-CMD ["npm", "run", "start:prod"]
+FROM node:20-alpine AS builder
+RUN apk add --no-cache python3 make g++ libc6-compat
+WORKDIR /app
+COPY package*.json ./
+# Install all deps for build (dev+prod)
+RUN npm ci
+COPY prisma/ ./prisma/
+COPY src/ ./src/
+COPY nest-cli.json ./
+COPY tsconfig*.json ./
+COPY eslint.config.mjs ./
+# Generate Prisma client and build
+RUN npx prisma generate && npm run build
+# Prune to production deps only to reduce size
+RUN npm prune --omit=dev && npm cache clean --force
+
+FROM node:20-alpine AS runner
+ENV NODE_ENV=production
+# Minimal init for proper signal handling
+RUN apk add --no-cache tini
+# Create app user/group
+RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup -s /sbin/nologin -h /home/appuser
+WORKDIR /app
+# Copy pruned node_modules and runtime artifacts
+COPY --from=builder --chown=root:appgroup /app/node_modules ./node_modules
+COPY --from=builder --chown=root:appgroup /app/dist ./dist
+COPY --from=builder --chown=root:appgroup /app/prisma ./prisma
+COPY --from=builder --chown=root:appgroup /app/package*.json ./
+# Tighten perms (preserve exec bits)
+RUN chown -R root:appgroup . && chmod -R o-rwx . && \
+    find . -type f -exec chmod g-w {} \; && find . -type d -exec chmod g-w {} \; && \
+    mkdir -p /app/logs /app/temp && chown appuser:appgroup /app/logs /app/temp && chmod 755 /app/logs /app/temp
+USER appuser
+EXPOSE 7000
+ENTRYPOINT ["/sbin/tini","--"]
+CMD ["node","dist/main.js"]
🧹 Nitpick comments (2)
.dockerignore (1)

79-88: Ignore common cloud credentials directories

Prevents accidental leakage of local cloud creds into build context.

 # Keys/credentials
 *.pem
 *.key
 *.p12
 *.pfx
 *.crt
 id_rsa*
 id_ed25519*
 .ssh/
+.aws/
+.kube/
+.azure/
+.config/gcloud/
+.git-credentials
+.gitconfig
Dockerfile (1)

22-25: Remove build-time debug ls

Reduces noise and doesn’t add value in final image layers.

-RUN ls -la prisma/schema.prisma && \
-    npx prisma generate && \
+RUN npx prisma generate && \
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 0fb24fa and c7c3812.

📒 Files selected for processing (3)
  • .dockerignore (1 hunks)
  • Dockerfile (1 hunks)
  • src/main.ts (1 hunks)
🔇 Additional comments (1)
Dockerfile (1)

37-41: No Dockerfile CMD changes needed
Verified package.json includes a start:prod script, so CMD ["npm", "run", "start:prod"] will work as intended.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Dockerfile (1)

16-16: Non-login runtime user — good hardening

Using a dedicated user with nologin and home dir is the right call.

🧹 Nitpick comments (2)
src/main.ts (1)

53-54: Handle PORT=0 and validate range (minor)

Current fallback treats 0 as falsy. Preserve 0 (ephemeral) and guard invalid values.

- const port = parseInt(process.env.PORT ?? '', 10) || 7000;
+ const p = Number(process.env.PORT);
+ const port = Number.isFinite(p) && p >= 0 && p <= 65535 ? p : 7000;
Dockerfile (1)

24-28: Adopt tini as PID 1 for proper signal handling and zombie reaping (optional)

Improves shutdown on SIGTERM in containers.

 # runtime dirs
 RUN mkdir -p /app/logs /app/temp && chown appuser:appgroup /app/logs /app/temp && chmod 755 /app/logs /app/temp
+RUN apk add --no-cache tini
 USER appuser
 EXPOSE 7000
+ENTRYPOINT ["/sbin/tini","--"]
 CMD ["node", "dist/main.js"]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c7c3812 and 21750a5.

📒 Files selected for processing (2)
  • Dockerfile (1 hunks)
  • src/main.ts (1 hunks)
🔇 Additional comments (2)
src/main.ts (1)

52-54: Port binding and 0.0.0.0 default look good

Binding explicitly to 0.0.0.0 and defaulting to 7000 improves container reachability and keeps Dockerfile/app consistent.

Dockerfile (1)

27-28: Port alignment with app (7000) — consistent

EXPOSE matches src/main.ts default/bind. Good.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
Dockerfile (3)

5-5: Remove --ignore-scripts; it breaks Prisma/native deps generation.

Lifecycle hooks are needed for @prisma/client and other native modules. Running with --ignore-scripts risks a broken runtime even if the builder ran prisma generate.

Apply:

-RUN npm ci --ignore-scripts
+RUN npm ci
-RUN chmod 644 package*.json && npm ci --omit=dev --ignore-scripts && npm cache clean --force
+RUN chmod 644 package*.json && npm ci --omit=dev && npm cache clean --force

Also applies to: 19-19


21-23: Ensure Prisma client artifacts are present at runtime.

Right now only engines (.prisma) are copied; the generated @prisma/client is not. Without postinstall (currently disabled) or copying @prisma/client, Prisma will fail at runtime.

Pick one:

A) Allow scripts (preferred; see prior comment) and don’t copy engines:

-COPY --from=builder --chown=root:appgroup /app/node_modules/.prisma/ ./node_modules/.prisma/

B) Keep scripts disabled (not recommended) and copy the generated client too:

+COPY --from=builder --chown=root:appgroup /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/

24-26: Don’t strip execute bits; breaks Prisma engines and any binaries.

chmod -R 644 prisma/ dist/ node_modules/.prisma/ removes x-bits from engine binaries. This will crash at startup.

Safer perms (preserve executables):

-RUN chmod -R 644 prisma/ dist/ node_modules/.prisma/ && \
-    find prisma/ dist/ node_modules/.prisma/ -type d -exec chmod 755 {} \; && \
+RUN find prisma/ dist/ -type d -exec chmod 755 {} \; && \
+    find prisma/ dist/ -type f -exec chmod g-w {} \; && \
     mkdir -p /app/logs /app/temp && chown appuser:appgroup /app/logs /app/temp && chmod 755 /app/logs /app/temp

If you keep copying .prisma/, exclude it from chmod entirely.

🧹 Nitpick comments (2)
Dockerfile (2)

18-19: Minor: avoid chmod of package.json; let npm manage perms.

The explicit 644 is unnecessary; drop for simplicity.

-COPY --chown=root:appgroup package*.json ./
-RUN chmod 644 package*.json && npm ci --omit=dev && npm cache clean --force
+COPY --chown=root:appgroup package*.json ./
+RUN npm ci --omit=dev && npm cache clean --force

2-2: Optional: install build tooling only when needed and cache npm.

Use a virtual package for build deps and leverage Dockerfile cache mounts for faster builds.

Example:

-RUN apk add --no-cache g++ libc6-compat make python3
+RUN apk add --no-cache g++ libc6-compat make python3
+# or: RUN --mount=type=cache,target=/root/.npm npm ci
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 21750a5 and 314d83a.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🔇 Additional comments (5)
Dockerfile (5)

16-16: Non-root user with nologin looks good.
Hardened user setup is correct and follows least-privilege.


11-11: Confirm builder can run prisma generate.

With npm ci now running scripts, this is fine. If you ever reintroduce --ignore-scripts, some dev deps requiring builds may fail here.

Would you like a quick check script to scan package.json for native deps that typically need install scripts?


21-21: Verify need to ship prisma/schema in runtime.

If you rely on @prisma/client postinstall in runner, keeping prisma/ is necessary; otherwise, drop it to reduce leakage.


27-27: USER appuser is correct.

Least-privilege runtime is enforced.


28-29: Port exposure matches application listen port. Verified that src/main.ts defaults to port 7000, so EXPOSE 7000 and the CMD are correctly aligned.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Dockerfile (1)

19-19: Avoid --ignore-scripts in runner; risk breaking native deps and Prisma hooks.

Either rely on copied artifacts fully or allow lifecycle scripts to run.

Apply:

-RUN npm ci --omit=dev --ignore-scripts && npm cache clean --force
+RUN npm ci --omit=dev && npm cache clean --force

To verify no prod deps need install scripts, run:

#!/bin/bash
set -euo pipefail
python - <<'PY'
import json,sys
p=json.load(open("package.json"))
deps={}
for k in ("dependencies","optionalDependencies"):
  deps.update(p.get(k) or {})
needs={"prisma","@prisma/client","bcrypt","argon2","sharp","canvas","better-sqlite3","sqlite3","node-sass","sass-embedded"}
hit=sorted(set(deps).intersection(needs))
print("deps_requiring_scripts:", ", ".join(hit) if hit else "none")
PY
🧹 Nitpick comments (2)
Dockerfile (2)

21-24: Avoid forcing 0755 on copied files.

--chmod=755 marks all files executable unnecessarily and complicates later permission fixes.

Apply:

-COPY --chown=root:appgroup --chmod=755 prisma/ ./prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/dist/ ./dist/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/.prisma/ ./node_modules/.prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/
+COPY --chown=root:appgroup prisma/ ./prisma/
+COPY --from=builder --chown=root:appgroup /app/dist/ ./dist/
+COPY --from=builder --chown=root:appgroup /app/node_modules/.prisma/ ./node_modules/.prisma/
+COPY --from=builder --chown=root:appgroup /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/

28-29: Consider adding a liveness probe and tini for production
Bind to 0.0.0.0:7000 is confirmed in src/main.ts. To improve container reliability, install tini and add a Docker HEALTHCHECK:

FROM node:20-alpine AS runner
ENV NODE_ENV=production
+RUN apk add --no-cache tini
USER appuser
EXPOSE 7000
+HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
+  CMD wget -qO- http://127.0.0.1:7000/health || exit 1
-CMD ["node", "dist/main.js"]
+ENTRYPOINT ["/sbin/tini","--"]
+CMD ["node", "dist/main.js"]

Adjust the health endpoint path as needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 314d83a and 626571f.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🔇 Additional comments (2)
Dockerfile (2)

1-12: Multi-stage Alpine build and CI flow look solid.

Builder stage is clean and minimal; good use of npm ci and prisma generate before build.


16-16: Non-root, nologin user: LGTM.

Good hardening with explicit UID/GID and nologin shell.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
Dockerfile (4)

16-16: Good hardening: non-root user with nologin shell.

User creation with /sbin/nologin and explicit UID/GID looks solid.


5-5: Remove --ignore-scripts in builder; it breaks deps needing lifecycle hooks (incl. Prisma).

Lifecycle scripts are needed for native/build-time deps; skipping them is risky even if you later run prisma generate.

Apply:

-RUN npm ci --ignore-scripts
+RUN npm ci

19-19: Do not use --ignore-scripts in the runner; allow postinstall hooks.

Other prod deps may require scripts; you’re already running as non-root, so keep scripts enabled and still omit devDeps.

Apply:

-RUN npm ci --omit=dev --ignore-scripts && npm cache clean --force
+RUN npm ci --omit=dev && npm cache clean --force

21-26: Danger: stripping execute bits from Prisma engines; also redundant perms churn.

  • find ... -exec chmod 644 over .prisma and @prisma/client removes x-bits required by Prisma binaries → runtime crash.
  • You first COPY --chmod=755 then immediately downgrade to 644; pick one consistent, safe policy.

Apply one of the safe fixes (preferred: don’t touch node_modules and only chmod non-exec files you control):

-COPY --chown=root:appgroup --chmod=755 prisma/ ./prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/dist/ ./dist/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/.prisma/ ./node_modules/.prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/
-RUN find prisma/ dist/ node_modules/.prisma/ node_modules/@prisma/client/ -type f -exec chmod 644 {} \; && \
-    mkdir -p /app/logs /app/temp && chown appuser:appgroup /app/logs /app/temp && chmod 755 /app/logs /app/temp
+COPY --chown=root:appgroup prisma/ ./prisma/
+COPY --from=builder --chown=root:appgroup /app/dist/ ./dist/
+COPY --from=builder --chown=root:appgroup /app/node_modules/.prisma/ ./node_modules/.prisma/
+COPY --from=builder --chown=root:appgroup /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/
+RUN find prisma/ dist/ -type f ! -perm /111 -exec chmod 644 {} \; && \
+    mkdir -p /app/logs /app/temp && chown appuser:appgroup /app/logs /app/temp && chmod 755 /app/logs /app/temp
🧹 Nitpick comments (3)
Dockerfile (3)

1-1: Optional: pin base image by digest for supply‑chain stability.

Use an immutable digest to avoid surprise upgrades.

Apply:

-FROM node:20-alpine AS builder
+FROM node:20-alpine@sha256:<digest> AS builder

(and same for runner).


13-14: Optional: add HEALTHCHECK and OCI labels.

Improves operability and provenance in registries.

Example:

 FROM node:20-alpine AS runner
 ENV NODE_ENV=production
+LABEL org.opencontainers.image.source="https://github.com/tekdi/ubi-strapi-provider-mw" \
+      org.opencontainers.image.title="ubi-strapi-provider-mw" \
+      org.opencontainers.image.description="Strapi Provider MW"
@@
 EXPOSE 7000
+HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \
+  CMD wget -qO- http://127.0.0.1:7000/health || exit 1
 CMD ["node", "dist/main.js"]

21-24: Nit: avoid --chmod=755 on JS/prisma sources.

755 on source files is unnecessary; default read-only perms are fine.

Apply:

-COPY --chown=root:appgroup --chmod=755 prisma/ ./prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/dist/ ./dist/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/.prisma/ ./node_modules/.prisma/
-COPY --from=builder --chown=root:appgroup --chmod=755 /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/
+COPY --chown=root:appgroup prisma/ ./prisma/
+COPY --from=builder --chown=root:appgroup /app/dist/ ./dist/
+COPY --from=builder --chown=root:appgroup /app/node_modules/.prisma/ ./node_modules/.prisma/
+COPY --from=builder --chown=root:appgroup /app/node_modules/@prisma/client/ ./node_modules/@prisma/client/
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 626571f and b92af66.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🔇 Additional comments (1)
Dockerfile (1)

28-29: No changes needed: port binding matches EXPOSE 7000
The application already binds to 0.0.0.0 on port 7000 by default (app.listen(port, '0.0.0.0') with fallback to 7000), matching the Dockerfile’s EXPOSE and CMD.

@sonarqubecloud
Copy link

@rajnishdargan rajnishdargan changed the title Issue #245978 feat: Provider BE - Security Hotspot fixes Issue #245978 feat: Provider BE - Docker Security Hotspot fixes Aug 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant