Skip to content

feat: add megalinter to CI workflow#12

Open
ryanachten wants to merge 1 commit into
mainfrom
feature/megalinter-worktree
Open

feat: add megalinter to CI workflow#12
ryanachten wants to merge 1 commit into
mainfrom
feature/megalinter-worktree

Conversation

@ryanachten

@ryanachten ryanachten commented Apr 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds MegaLinter as a separate job in CI workflow
  • Validates only changed files (delta mode) for incremental improvements
  • Includes SARIF reporter for security findings

Summary by CodeRabbit

  • New Features

    • Added container health checks for improved deployment monitoring and orchestration.
    • Exposed port 8081 for ToyDbRouting service.
  • Chores

    • Enhanced container security by running services as non-root user and restricting Linux capabilities.
    • Integrated automated security scanning into the CI/CD pipeline.

@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@ryanachten has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 21 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 21 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5552dfdf-22c4-4c17-89d8-4d3f41b649a8

📥 Commits

Reviewing files that changed from the base of the PR and between 173580d and 0810e11.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • .mega-linter.yml
  • ToyDb/Dockerfile
  • ToyDbRouting/Dockerfile
  • docker-compose.yml
📝 Walkthrough

Walkthrough

This PR introduces security hardening and operational monitoring across the infrastructure. Changes include enabling security scanning via MegaLinter in the CI pipeline, configuring Docker containers to run as non-root users with dropped Linux capabilities, adding health checks to Docker services, and disabling specific linters in the MegaLinter configuration.

Changes

Cohort / File(s) Summary
CI/Security Scanning
.github/workflows/ci.yml, .mega-linter.yml
Added GitHub Actions workflow permissions and a new mega-lint job running security scanning via MegaLinter v7.13.0. Introduced MegaLinter configuration to disable specific repository scanners (DevSkim, Grype, Checkov, KICS).
Container Runtime Hardening
ToyDb/Dockerfile, ToyDbRouting/Dockerfile
Added configurable HEALTHCHECK_URL build argument and Docker HEALTHCHECK instructions to monitor container health via periodic curl requests. Updated ToyDbRouting/Dockerfile to enforce non-root execution with USER app.
Docker Compose Services
docker-compose.yml
Applied security constraints (no-new-privileges: true, cap_drop: [ALL]) and health checks across toydbrouting and all toydb replica services. Added port mapping for toydbrouting service (8081:8081).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~13 minutes

Poem

🐰 Hop hop, security's here to stay,
Containers locked tight, caps dropped away,
Health checks flutter, like ears in the breeze,
MegaLinter hops through, puts minds at ease!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add megalinter to CI workflow' clearly summarizes the main change: introducing MegaLinter to the CI workflow. While the PR also includes security hardening in Dockerfiles and docker-compose, the title correctly identifies the primary objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/megalinter-worktree

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread ToyDb/Dockerfile Outdated
USER app
WORKDIR /app
COPY --from=publish /app/publish .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:8080/diagnostics/health || exit 1

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I'm not sure we shoul be tying our healthchecks to localhost - this would work in CI and locally, but how would this work in production?

Comment thread ToyDbRouting/Dockerfile Outdated
USER app
WORKDIR /app
COPY --from=publish /app/publish .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:8080/diagnostics/health || exit 1

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Same comment here about healthchecks and localhost

Comment thread docker-compose.yml
cap_drop:
- ALL
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/diagnostics/health"]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This compose file is only really intended for local dev, so it's probably fine that we use localhost in healthcheck here

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (4)
.github/workflows/ci.yml (1)

9-13: Tighten workflow-level permissions to least privilege

Line 9 through Line 13 grant issues: read and pull-requests: read globally, but the shown jobs do not appear to need them. Keep only required scopes at workflow level and elevate per-job when necessary.

Proposed fix
 permissions:
   contents: read
-  issues: read
-  pull-requests: read
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 9 - 13, The workflow-level permissions
currently grant unnecessary scopes (permissions: issues: read and pull-requests:
read); remove those from the top-level permissions block so only the required
narrow scope (e.g., contents: read) remains, and if any specific job needs issue
or PR access add a per-job permissions block with issues: read or pull-requests:
read inside that job; update references to the top-level permissions keys
(permissions, contents, issues, pull-requests) and ensure no job relies on the
removed global scopes without adding per-job elevation.
ToyDb/Dockerfile (1)

29-29: USER app is duplicated in final stage.

Line 29 is redundant because FROM base AS final already inherits USER app from Line 5.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ToyDb/Dockerfile` at line 29, The final stage contains a redundant "USER app"
because "FROM base AS final" already inherits the USER set in the base stage;
remove the duplicate "USER app" statement in the final stage (the second
occurrence) so only the original USER set in the base stage remains, leaving the
rest of the final stage (FROM base AS final and its commands) unchanged.
docker-compose.yml (1)

27-122: Consider YAML anchors to remove repeated service policy blocks.

The same security_opt, cap_drop, and healthcheck blocks are duplicated across all ToyDb replicas. A shared anchor/alias would cut repetition and reduce drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 27 - 122, The compose file repeats identical
security_opt, cap_drop and healthcheck blocks across the ToyDb services
(toydb-p1-r1, toydb-p1-r2, toydb-p2-r1, toydb-p2-r2, toydb-p1-r3, toydb-p2-r3);
refactor by defining a YAML anchor (e.g., &toydb_defaults) containing
security_opt, cap_drop and healthcheck and then reference it in each service
with the alias (*toydb_defaults) or merge key (<<: *toydb_defaults) to remove
duplication while preserving the existing settings.
ToyDbRouting/Dockerfile (1)

28-28: Remove redundant USER app in final stage.

Line 28 repeats USER app already set in base (Line 5), so it can be dropped for clarity.

Diff
 FROM base AS final
 ARG HEALTHCHECK_URL=http://localhost:8080/diagnostics/health
-USER app
 WORKDIR /app
 COPY --from=publish /app/publish .
 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f ${HEALTHCHECK_URL} || exit 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ToyDbRouting/Dockerfile` at line 28, Remove the redundant USER app
declaration in the final Dockerfile stage: locate the final stage where "USER
app" is repeated (the second occurrence) and delete that line because the base
stage (USER app in the base stage) already sets the user; ensure no other
instructions rely on redefining the user in the final stage and keep only the
original USER app declaration in the base stage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Line 27: Replace the mutable tag in the workflow step that currently reads
"uses: oxsecurity/megalinter/flavors/security@v7.13.0" with the fixed commit SHA
to pin the action; update that string to use the commit SHA
b48455a119cc28045eee8f1e9d0a542a85e71f4f (i.e., "uses:
oxsecurity/megalinter/flavors/security@b48455a1...") so the CI references the
exact commit instead of the mutable v7.13.0 tag.
- Line 31: Remove the insecure environment variable by deleting the
DISABLE_HTTPS_CERTIFICATE_VALIDATION: "true" entry from the MegaLinter/CI step
configuration so TLS certificate validation is not disabled; locate the CI
workflow's MegaLinter/env block where DISABLE_HTTPS_CERTIFICATE_VALIDATION is
defined and remove that key entirely (or comment it out) and commit the change,
ensuring no other steps depend on this flag.

In @.mega-linter.yml:
- Around line 1-7: Remove the global disable entries in DISABLE_LINTERS and stop
overwriting exclusions via OVERWRITE_EXCLUSIONS; instead create per-linter
config files and add targeted suppressions: add a .checkov.yml with skip-check
entries for REPOSITORY_CHECKOV, add a .grype.yaml with ignore rules for
REPOSITORY_GRYPE, and add a .devskim.json with rule/glob suppressions for
REPOSITORY_DEVSKIM; then delete the DISABLE_LINTERS list (or remove those
specific entries) and remove the OVERWRITE_EXCLUSIONS: "" line from
.mega-linter.yml so MegaLinter runs but respects the new per-linter configs.

In `@docker-compose.yml`:
- Around line 21-26: The healthcheck in docker-compose for the toydb-routing
service calls /diagnostics/health which is only registered when
app.Environment.IsDevelopment() in ToyDbRouting/Program.cs; add the
ASPNETCORE_ENVIRONMENT=Development environment variable to the toydb-routing
service in docker-compose so the endpoint is available (i.e., add an environment
section with ASPNETCORE_ENVIRONMENT set to Development for the toydb-routing
service).
- Around line 37-42: The healthchecks in docker-compose point to a non-existent
HTTP endpoint (/diagnostics/health) while the app only registers a gRPC health
service via MapGrpcHealthChecksService in Program.cs; fix by either adding an
HTTP health probe endpoint in the service (e.g., register an HTTP route or
MapHealthChecks/MapGet that returns 200 for "/diagnostics/health" alongside
MapGrpcHealthChecksService) or by changing the docker-compose healthcheck tests
to use a gRPC health probe (use a grpc health probe binary or grpcurl against
the gRPC health service) and update the healthcheck test commands for all ToyDb
service entries accordingly.

In `@ToyDb/Dockerfile`:
- Around line 28-32: The Dockerfile HEALTHCHECK is probing an HTTP URL that the
app doesn't expose; update either the app or the Dockerfile: either add an HTTP
health endpoint in Program.cs (e.g., MapGet("/diagnostics/health", () =>
Results.Ok()) or map an HTTP health check that mirrors
MapGrpcHealthChecksService) so the existing HEALTHCHECK URL works, or replace
the Dockerfile HEALTHCHECK with a gRPC-aware probe (e.g., install and invoke
grpc_health_probe or use grpcurl against the gRPC endpoint) and update
HEALTHCHECK/CMD accordingly (referencing HEALTHCHECK, HEALTHCHECK_URL, and
MapGrpcHealthChecksService to locate the relevant code).

In `@ToyDbRouting/Dockerfile`:
- Around line 27-31: The Dockerfile HEALTHCHECK currently probes
/diagnostics/health which is only registered inside the development-only branch
in Program.cs (the app.Environment.IsDevelopment() block), causing health checks
to fail in production; fix by either moving the health endpoint registration out
of the development-only branch (register the diagnostics/health endpoint
unconditionally in Program.cs or add a production-safe endpoint like /health) or
change the Dockerfile HEALTHCHECK to probe an endpoint that exists in
production, and update the Dockerfile HEALTHCHECK (the HEALTHCHECK --interval...
CMD line) or the endpoint registration in Program.cs accordingly so the probed
path exists in non-development environments.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 9-13: The workflow-level permissions currently grant unnecessary
scopes (permissions: issues: read and pull-requests: read); remove those from
the top-level permissions block so only the required narrow scope (e.g.,
contents: read) remains, and if any specific job needs issue or PR access add a
per-job permissions block with issues: read or pull-requests: read inside that
job; update references to the top-level permissions keys (permissions, contents,
issues, pull-requests) and ensure no job relies on the removed global scopes
without adding per-job elevation.

In `@docker-compose.yml`:
- Around line 27-122: The compose file repeats identical security_opt, cap_drop
and healthcheck blocks across the ToyDb services (toydb-p1-r1, toydb-p1-r2,
toydb-p2-r1, toydb-p2-r2, toydb-p1-r3, toydb-p2-r3); refactor by defining a YAML
anchor (e.g., &toydb_defaults) containing security_opt, cap_drop and healthcheck
and then reference it in each service with the alias (*toydb_defaults) or merge
key (<<: *toydb_defaults) to remove duplication while preserving the existing
settings.

In `@ToyDb/Dockerfile`:
- Line 29: The final stage contains a redundant "USER app" because "FROM base AS
final" already inherits the USER set in the base stage; remove the duplicate
"USER app" statement in the final stage (the second occurrence) so only the
original USER set in the base stage remains, leaving the rest of the final stage
(FROM base AS final and its commands) unchanged.

In `@ToyDbRouting/Dockerfile`:
- Line 28: Remove the redundant USER app declaration in the final Dockerfile
stage: locate the final stage where "USER app" is repeated (the second
occurrence) and delete that line because the base stage (USER app in the base
stage) already sets the user; ensure no other instructions rely on redefining
the user in the final stage and keep only the original USER app declaration in
the base stage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9b2c3c9b-04ff-42cb-9723-e2501fea3372

📥 Commits

Reviewing files that changed from the base of the PR and between 6f721a9 and 173580d.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • .mega-linter.yml
  • ToyDb/Dockerfile
  • ToyDbRouting/Dockerfile
  • docker-compose.yml

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .mega-linter.yml
Comment on lines +1 to +7
DISABLE_LINTERS:
- REPOSITORY_DEVSKIM
- REPOSITORY_GRYPE
- REPOSITORY_CHECKOV
- REPOSITORY_KICS

OVERWRITE_EXCLUSIONS: ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For MegaLinter v7 security flavor, what is the recommended way to handle false positives for REPOSITORY_CHECKOV, REPOSITORY_GRYPE, and REPOSITORY_DEVSKIM: disable entire linters or configure targeted exclusions/rule suppressions?

💡 Result:

For MegaLinter v7 security flavor, the recommended way to handle false positives for REPOSITORY_CHECKOV, REPOSITORY_GRYPE, and REPOSITORY_DEVSKIM is to configure targeted exclusions and rule suppressions using each linter's native configuration files, rather than disabling the entire linters. These repository-level linters operate in 'project' CLI lint mode, scanning the entire repository root, so MegaLinter's general file filters (like FILTER_REGEX_EXCLUDE) do not apply—filtering and suppression must be done via the linters' own mechanisms. - REPOSITORY_CHECKOV: Use a .checkov.yml file at the repository root to skip specific checks, define exclusions, or configure rules (e.g., skip-check: [CKV_AWS_123]). MegaLinter looks for REPOSITORY_CHECKOV_CONFIG_FILE (.checkov.yml) in the workspace or uses a default. - REPOSITORY_GRYPE: Use a .grype.yaml (or .grype.yml) file to ignore specific vulnerabilities or paths (e.g., ignore: vulnerability: CVE-XXXX-XXXX). Configured via REPOSITORY_GRYPE_CONFIG_FILE. - REPOSITORY_DEVSKIM: Use a .devskim.json file for Glob exclusions (e.g., Glob: ["/node_modules/"]) or rule suppression. Supports --ignore-rules-ids via arguments, but config file is preferred. Configured via REPOSITORY_DEVSKIM_CONFIG_FILE. Disabling entire linters via DISABLE_LINTERS is possible but not recommended for false positives, as it eliminates all scanning. Official MegaLinter docs emphasize linter-specific config files for project-mode linters, with explicit notes that "filtering can not be done using MegaLinter configuration variables, it must be done using [linter] configuration."

Citations:


Use linter-specific configuration files instead of global disables

Lines 1–7 disable multiple security scanners globally, which contradicts the PR objective of adding MegaLinter security checks. Per MegaLinter v7 documentation, the recommended approach for handling false positives is to configure targeted suppressions in each linter's native configuration file:

  • REPOSITORY_CHECKOV: Use .checkov.yml with skip-check entries
  • REPOSITORY_GRYPE: Use .grype.yaml with ignore entries for specific vulnerabilities
  • REPOSITORY_DEVSKIM: Use .devskim.json for Glob exclusions and rule suppression

Remove global linter disables and create these configuration files at the repository root instead. Also remove OVERWRITE_EXCLUSIONS: "" unless explicitly required by your setup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.mega-linter.yml around lines 1 - 7, Remove the global disable entries in
DISABLE_LINTERS and stop overwriting exclusions via OVERWRITE_EXCLUSIONS;
instead create per-linter config files and add targeted suppressions: add a
.checkov.yml with skip-check entries for REPOSITORY_CHECKOV, add a .grype.yaml
with ignore rules for REPOSITORY_GRYPE, and add a .devskim.json with rule/glob
suppressions for REPOSITORY_DEVSKIM; then delete the DISABLE_LINTERS list (or
remove those specific entries) and remove the OVERWRITE_EXCLUSIONS: "" line from
.mega-linter.yml so MegaLinter runs but respects the new per-linter configs.

Comment thread docker-compose.yml
Comment thread docker-compose.yml
Comment on lines +37 to +42
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/diagnostics/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm ToyDb does not expose /diagnostics/health and uses gRPC health checks
rg -n -C3 'MapGet\("/diagnostics/health"|MapGrpcHealthChecksService\(' ToyDb/Program.cs

Repository: ryanachten/ToyDb

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Search for all health-related endpoint mappings in Program.cs
echo "=== All health-related mappings in ToyDb/Program.cs ==="
rg -n -i 'health|MapGet|MapPost|MapPut|Map' ToyDb/Program.cs | head -30

echo -e "\n=== Healthcheck lines in docker-compose.yml ==="
rg -n 'healthcheck:|test:' docker-compose.yml -A 1

Repository: ryanachten/ToyDb

Length of output: 1331


ToyDb healthchecks target a non-existent HTTP route.

At lines 22, 38, 54, 70, 86, 102, 118, all ToyDb services probe /diagnostics/health via HTTP curl, but ToyDb/Program.cs exposes only a gRPC health service (MapGrpcHealthChecksService at line 20). The /diagnostics/health HTTP endpoint does not exist, causing all healthchecks to fail. Either add an HTTP health endpoint or reconfigure the docker-compose probes to use gRPC health checks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 37 - 42, The healthchecks in docker-compose
point to a non-existent HTTP endpoint (/diagnostics/health) while the app only
registers a gRPC health service via MapGrpcHealthChecksService in Program.cs;
fix by either adding an HTTP health probe endpoint in the service (e.g.,
register an HTTP route or MapHealthChecks/MapGet that returns 200 for
"/diagnostics/health" alongside MapGrpcHealthChecksService) or by changing the
docker-compose healthcheck tests to use a gRPC health probe (use a grpc health
probe binary or grpcurl against the gRPC health service) and update the
healthcheck test commands for all ToyDb service entries accordingly.

Comment thread ToyDb/Dockerfile Outdated
Comment thread ToyDbRouting/Dockerfile Outdated
- Add megalinter security job to CI workflow
- Pin action to commit SHA for security
- Disable KICS, DEVSKIM, TRIVY, GRYPE, SEMGREP due to CI compatibility issues
- Add security_opt, cap_drop, healthcheck to docker-compose
- Add ASPNETCORE_ENVIRONMENT=Development to compose services
- Remove HEALTHCHECK from Dockerfiles (configure at orchestration level)
@ryanachten
ryanachten force-pushed the feature/megalinter-worktree branch from 8031616 to 0810e11 Compare April 3, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant