Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ CORS_MAX_AGE=3600
# CSP_CONNECT_SRC: Allowed origins for fetch/XHR/WebSocket connections.
# Include 'self', your backend API URL, and auth provider (Keycloak).
# Example: 'self' https://api.example.com https://auth.example.com
# CSP_OBJECT_SRC: Allowed sources for <object>, <embed>, <applet> elements.
# Use 'self' blob: to allow PDF preview with blob URLs, or 'none' for strict security.
CSP_SCRIPT_SRC="'self' 'unsafe-inline'"
CSP_STYLE_SRC="'self' 'unsafe-inline'"
CSP_CONNECT_SRC="'self' https://api.example.com https://auth.example.com"
CSP_OBJECT_SRC="'self' blob:"
Comment on lines +95 to +100
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Well-documented CSP_OBJECT_SRC configuration.

The documentation clearly explains the purpose of CSP_OBJECT_SRC, providing context for both the permissive default ('self' blob:) for PDF previews and the strict alternative ('none').

The static analysis warning about key ordering is purely cosmetic. If you want to maintain alphabetical ordering for easier scanning, consider reordering the CSP variables alphabetically:

🔎 Optional style improvement
 CSP_SCRIPT_SRC="'self' 'unsafe-inline'"
 CSP_STYLE_SRC="'self' 'unsafe-inline'"
-CSP_CONNECT_SRC="'self' https://api.example.com https://auth.example.com"
-CSP_OBJECT_SRC="'self' blob:"
+CSP_CONNECT_SRC="'self' https://api.example.com https://auth.example.com"
+CSP_OBJECT_SRC="'self' blob:"

However, the current grouping (keeping related CSP directives together) is also reasonable and may be more intuitive than strict alphabetization.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 99-99: [UnorderedKey] The CSP_CONNECT_SRC key should go before the CSP_SCRIPT_SRC key

(UnorderedKey)


[warning] 100-100: [UnorderedKey] The CSP_OBJECT_SRC key should go before the CSP_SCRIPT_SRC key

(UnorderedKey)

🤖 Prompt for AI Agents
.env.example lines 95-100: static analysis flagged CSP key ordering as a
cosmetic issue; to satisfy the linter, reorder the CSP_* variables
alphabetically (e.g., CSP_CONNECT_SRC, CSP_OBJECT_SRC, CSP_SCRIPT_SRC,
CSP_STYLE_SRC) while keeping the existing values and comments unchanged.


# ----------------------------
# Service Versions & Helpers
Expand Down
4 changes: 3 additions & 1 deletion client/apps/webapp/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
FROM nginxinc/nginx-unprivileged:alpine AS production

# Switch to root temporarily to install packages and configure
USER root

Check failure on line 60 in client/apps/webapp/Dockerfile

View workflow job for this annotation

GitHub Actions / Lint Meta-Files

DL3002 warning: Last USER should not be root

Check failure on line 60 in client/apps/webapp/Dockerfile

View workflow job for this annotation

GitHub Actions / Validate Dockerfiles

DL3002 warning: Last USER should not be root

# Install required packages:
# curl: for healthcheck
Expand All @@ -74,7 +74,7 @@
RUN chmod +x /docker-entrypoint.sh

# Ensure config directory is writable by nginx user for envsubst output
RUN chown -R nginx:nginx /etc/nginx/conf.d

Check failure on line 77 in client/apps/webapp/Dockerfile

View workflow job for this annotation

GitHub Actions / Lint Meta-Files

DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.

Check failure on line 77 in client/apps/webapp/Dockerfile

View workflow job for this annotation

GitHub Actions / Validate Dockerfiles

DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.

# Copy built files from build stage with proper ownership
COPY --from=build --chown=nginx:nginx /app/client/apps/webapp/dist /usr/share/nginx/html
Expand All @@ -85,10 +85,12 @@
# CSP_SCRIPT_SRC: 'self' 'sha256-HASH1' 'sha256-HASH2' (remove 'unsafe-inline')
# CSP_STYLE_SRC: 'self' 'sha256-HASH1' (remove 'unsafe-inline')
# CSP_CONNECT_SRC: 'self' https://api.yourdomain.com https://auth.yourdomain.com
# CSP_OBJECT_SRC: 'self' blob: (for PDF preview), or 'none' for strict security
ENV BACKEND_URL=https://backend.localhost \
CSP_SCRIPT_SRC="'self' 'unsafe-inline'" \
CSP_STYLE_SRC="'self' 'unsafe-inline'" \
CSP_CONNECT_SRC="'self' https://backend.localhost"
CSP_CONNECT_SRC="'self' https://backend.localhost" \
CSP_OBJECT_SRC="'self' blob:"

# Expose port 8080 (non-privileged port, nginx-unprivileged default)
EXPOSE 8080
Expand Down
7 changes: 5 additions & 2 deletions client/apps/webapp/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ MISSING_VARS=""
[ -z "$CSP_SCRIPT_SRC" ] && MISSING_VARS="$MISSING_VARS CSP_SCRIPT_SRC"
[ -z "$CSP_STYLE_SRC" ] && MISSING_VARS="$MISSING_VARS CSP_STYLE_SRC"
[ -z "$CSP_CONNECT_SRC" ] && MISSING_VARS="$MISSING_VARS CSP_CONNECT_SRC"
[ -z "$CSP_OBJECT_SRC" ] && MISSING_VARS="$MISSING_VARS CSP_OBJECT_SRC"

if [ -n "$MISSING_VARS" ]; then
echo "❌ ERROR: Required environment variables are not set:$MISSING_VARS" >&2
Expand All @@ -34,6 +35,7 @@ if [ -n "$MISSING_VARS" ]; then
echo " CSP_SCRIPT_SRC - CSP script-src directive (e.g., 'self' 'unsafe-inline')" >&2
echo " CSP_STYLE_SRC - CSP style-src directive (e.g., 'self' 'unsafe-inline')" >&2
echo " CSP_CONNECT_SRC - CSP connect-src directive (e.g., 'self' https://api.example.com)" >&2
echo " CSP_OBJECT_SRC - CSP object-src directive (e.g., 'self' blob: for PDF preview)" >&2
echo "" >&2
echo "See Dockerfile ENV section or infra/.env.example for defaults." >&2
exit 1
Expand All @@ -44,10 +46,11 @@ echo " BACKEND_URL: $BACKEND_URL"
echo " CSP_SCRIPT_SRC: $CSP_SCRIPT_SRC"
echo " CSP_STYLE_SRC: $CSP_STYLE_SRC"
echo " CSP_CONNECT_SRC: $CSP_CONNECT_SRC"
echo " CSP_OBJECT_SRC: $CSP_OBJECT_SRC"

echo "Substituting environment variables in nginx configuration..."
envsubst '${BACKEND_URL} ${CSP_SCRIPT_SRC} ${CSP_STYLE_SRC} ${CSP_CONNECT_SRC}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
envsubst '${BACKEND_URL} ${CSP_SCRIPT_SRC} ${CSP_STYLE_SRC} ${CSP_CONNECT_SRC}' < /etc/nginx/conf.d/security-headers.conf.template > /etc/nginx/conf.d/security-headers.conf
envsubst '${BACKEND_URL} ${CSP_SCRIPT_SRC} ${CSP_STYLE_SRC} ${CSP_CONNECT_SRC} ${CSP_OBJECT_SRC}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
envsubst '${BACKEND_URL} ${CSP_SCRIPT_SRC} ${CSP_STYLE_SRC} ${CSP_CONNECT_SRC} ${CSP_OBJECT_SRC}' < /etc/nginx/conf.d/security-headers.conf.template > /etc/nginx/conf.d/security-headers.conf

echo "✅ Configuration files generated successfully"

Expand Down
4 changes: 3 additions & 1 deletion client/apps/webapp/security-headers.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
# - CSP_STYLE_SRC: CSP style-src directive (e.g., 'self' 'sha256-...')
# - CSP_CONNECT_SRC: CSP connect-src directive for allowed fetch/XHR/WebSocket origins
# (e.g., 'self' https://api.example.com https://auth.example.com)
# - CSP_OBJECT_SRC: CSP object-src directive for <object>, <embed>, <applet> elements
# (e.g., 'none' for maximum security, or 'self' blob: for PDF preview)

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src ${CSP_SCRIPT_SRC}; style-src ${CSP_STYLE_SRC}; img-src 'self' data: https:; font-src 'self' data:; connect-src ${CSP_CONNECT_SRC}; frame-ancestors 'self'; object-src 'none'; base-uri 'self'; form-action 'self'" always;
add_header Content-Security-Policy "default-src 'self'; script-src ${CSP_SCRIPT_SRC}; style-src ${CSP_STYLE_SRC}; img-src 'self' data: https:; font-src 'self' data:; connect-src ${CSP_CONNECT_SRC}; frame-ancestors 'self'; object-src ${CSP_OBJECT_SRC}; base-uri 'self'; form-action 'self'" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Loading