Skip to content

Code severity fixes & vulnerability fixes#369

Open
yashashkumar wants to merge 1 commit intomainfrom
vul-fixes-cts
Open

Code severity fixes & vulnerability fixes#369
yashashkumar wants to merge 1 commit intomainfrom
vul-fixes-cts

Conversation

@yashashkumar
Copy link

@yashashkumar yashashkumar commented Feb 10, 2026

Summary by CodeRabbit

  • Security Enhancements

    • Implemented HTTP Strict-Transport-Security headers across all API responses to enforce secure HTTPS communication and prevent protocol downgrade attacks.
  • Backend Improvements

    • Enhanced request handling patterns and database credential management for improved system stability and reliability.

@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

Changes refactor request property access patterns across controllers using lodash _.get() for safer data extraction, update database connection authentication to use a credentials string format, and add HTTP Strict-Transport-Security (HSTS) headers to multiple response handlers.

Changes

Cohort / File(s) Summary
Configuration & Database Authentication
api-service/src/configs/ConnectionsConfig.ts, api-service/src/connections/databaseConnection.ts
Postgres configuration refactored to combine username and password into a single credentials string (username::password); database connection updated to use credentials format in Sequelize constructor arguments instead of separate username/password fields.
Controller Request Access Refactoring
api-service/src/controllers/Alerts/Alerts.ts, api-service/src/controllers/Alerts/Metric.ts, api-service/src/controllers/Alerts/Silence.ts, api-service/src/controllers/DatasetRead/DatasetRead.ts, api-service/src/controllers/NotificationChannel/Notification.ts, api-service/src/controllers/UpdateQueryTemplate/UpdateTemplateController.ts
Replaced direct request property access (req.body, req.params) with lodash _.get() for safer null-coalescing access patterns across create, update, delete, and fetch handlers.
Security Headers
api-service/src/helpers/ResponseHandler.ts, api-service/src/controllers/DataMetrics/DataMetricsController.ts, api-service/src/metrics/prometheus/index.ts
Added HTTP Strict-Transport-Security (HSTS) header with max-age=31536000 and includeSubDomains to response handlers and metrics endpoints for enforced secure transport.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Poem

🐰 With lodash guarding every gate,
Safe paths ensure a sturdy state,
Credentials merged in fortress code,
HSTS shields the data road,
Security blooms where safe access grows! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive terms like 'severity fixes' and 'vulnerability fixes' without specifying what was actually changed. Use a more specific title that describes the main changes, such as 'Replace direct property access with lodash _.get for safer access patterns' or 'Add HSTS headers and refactor database credential handling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vul-fixes-cts

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.

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: 5

Caution

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

⚠️ Outside diff range comments (2)
api-service/src/controllers/Alerts/Metric.ts (1)

22-22: ⚠️ Potential issue | 🟠 Major

Pre-existing bug: comma expression discards the status code in errorResponse.

This isn't introduced by your PR, but worth flagging since it exists in every handler in this file. The double parentheses in errorResponse((httpStatus.INTERNAL_SERVER_ERROR, { message: errorMessage })) create a comma expression — the status code 500 is evaluated and discarded, and only { message: errorMessage } is passed to errorResponse. This means all error responses likely use the default status rather than 500 explicitly.

The same pattern appears on lines 33, 56, 69, and 81.

Fix for all occurrences
-        next(errorResponse((httpStatus.INTERNAL_SERVER_ERROR, { message: errorMessage })))
+        next(errorResponse(httpStatus.INTERNAL_SERVER_ERROR, errorMessage))
api-service/src/controllers/NotificationChannel/Notification.ts (1)

101-101: ⚠️ Potential issue | 🟠 Major

Pre-existing bug: Notification.update() is not awaited.

The Notification.update(...) call on Line 101 is missing await. This means the response is sent before the database update completes. If the update fails, the error is unhandled (becomes an unhandled promise rejection), and the client already received a success response.

Suggested fix
-        Notification.update({ status: "live", updated_by: userID }, { where: { id } });
+        await Notification.update({ status: "live", updated_by: userID }, { where: { id } });
🤖 Fix all issues with AI agents
In `@api-service/src/configs/ConnectionsConfig.ts`:
- Around line 5-10: The postgres config currently exposes a single concatenated
credentials string (postgres.credentials) which relies on a fragile "::"
separator and mismatched base64 assumptions; change ConnectionsConfig to expose
separate fields postgres.username and postgres.password (avoid concatenation),
set defaults as plain strings (e.g., "postgres"), and update the consumer
(databaseConnection.ts which currently does Buffer.from(credentials[1],
'base64')) to read connectionConfig.postgres.password and perform base64
decoding there only if the environment contract requires it; ensure you remove
any split("::") usage and handle decoding explicitly (Buffer.from(password,
'base64').toString('utf-8')) or skip decoding if the password is plaintext.

In `@api-service/src/connections/databaseConnection.ts`:
- Around line 4-7: Guard the fragile credentials parsing in databaseConnection
by validating the split result before decoding: check
connectionConfig.postgres.credentials contains "::" and that credentials.length
>= 2 and credentials[1] is a non-empty string, then attempt Buffer.from(...)
inside a try/catch to surface a clear, actionable error (or rethrow) if base64
decoding fails; update the variables referenced (credentials and
decodedCredentials) so they aren’t computed from undefined and add a descriptive
error message that mentions connectionConfig.postgres.credentials format
expectations and suggests using separate username/password fields.

In `@api-service/src/controllers/DataMetrics/DataMetricsController.ts`:
- Line 29: Standardize the Strict-Transport-Security header name to the
canonical casing: update the header string usages (e.g., the res.setHeader call
in DataMetricsController.ts) so all files use 'Strict-Transport-Security' (not
'STRICT-TRANSPORT-SECURITY'); locate occurrences in ResponseHandler.ts and
prometheus/index.ts (search for setHeader or the header key constant) and
replace the uppercase variant with the canonical 'Strict-Transport-Security'
string so every module uses the same header name.

In `@api-service/src/controllers/NotificationChannel/Notification.ts`:
- Line 50: The handler in Notification.ts uses _.get(request.body, 'request',
{}) which inconsistently accesses request.body directly; change it to use safe
lodash access consistent with other handlers—e.g., retrieve the body via
_.get(request, 'body', {}) and then extract the nested request object (or use
_.get(request, 'body.request', {})) so the destructuring of limit, filters,
offset uses the same safe access pattern as other handlers.

In `@api-service/src/metrics/prometheus/index.ts`:
- Line 28: Standardize the HSTS header name and centralize its setting: replace
all occurrences of the all-caps header string with the canonical
"Strict-Transport-Security" and remove the duplicated res.setHeader calls inside
ResponseHandler.successResponse, ResponseHandler.errorResponse,
ResponseHandler.obsrvErrorResponse, ResponseHandler.flatResponse,
ResponseHandler.goneResponse, metricsScrapeHandler and dataMetrics; instead add
a single Express middleware mounted early (e.g., via app.use) that calls
res.setHeader('Strict-Transport-Security', 'max-age=31536000;
includeSubDomains') and calls next() so every response automatically gets the
header.
🧹 Nitpick comments (3)
api-service/src/controllers/DatasetRead/DatasetRead.ts (1)

22-24: Duplicated _.get extraction in validateRequest and datasetRead.

dataset_id, fields, and mode are extracted identically in both functions (lines 22-24 and 36-38). Consider extracting once in datasetRead and passing to validateRequest, or having validateRequest return the parsed values.

Also applies to: 36-38

api-service/src/controllers/Alerts/Metric.ts (1)

13-14: Redundant component override on line 14.

component is extracted from req.body.component on line 13, then the entire req.body (which already contains component) is spread, and component is overridden with the same value. The override is a no-op.

This is pre-existing, but since you're touching these lines, consider simplifying:

Suggested simplification
-        const component  = _.get(req, 'body.component');
-        const metricsBody = await Metrics.create({ ...(_.get(req, 'body')), component: component });
+        const metricsBody = await Metrics.create(_.get(req, 'body'));
api-service/src/controllers/Alerts/Alerts.ts (1)

15-15: Inconsistent application of _.get() pattern across the file.

Several handlers still use direct destructuring on req.params and req.query (Lines 71, 89, 90), while the changed handlers now use _.get(req, 'params.alertId'). If the goal is safer property access, the pattern should be applied uniformly. Otherwise, some handlers are protected and others are not, making the security posture inconsistent.

That said, req.body, req.params, and req.query are always present on Express Request objects after middleware parsing, so _.get() doesn't add meaningful safety here — it only adds verbosity. Consider whether this change is truly necessary.

Also applies to: 33-33, 60-60, 108-109, 120-120, 135-135

Comment on lines +5 to +10
postgres: Object.freeze({
host: env['postgres_host'] || "localhost",
port: env['postgres_port'] || 5432,
database: env['postgres_database'] || "obsrv",
credentials: `${env['postgres_username'] || "postgres"}::${env['postgres_password'] || "postgres"}`
}),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Credential string format creates multiple failure modes.

  1. Base64 assumption mismatch: The consumer (databaseConnection.ts Line 7) calls Buffer.from(credentials[1], 'base64') to decode the password portion. But the default value "postgres" on Line 9 is plaintext, not base64-encoded. This will silently produce garbled credentials and the DB connection will fail at startup.

  2. Fragile separator: "::" can appear in passwords. If a password contains ::, split("::") in the consumer will yield more than two elements, and credentials[1] will be only a partial password — a silent data corruption bug.

  3. No security benefit: Concatenating credentials into one string only to split them in the consumer adds indirection without any hardening. The values still reside in plaintext in process memory. If the intent is to accept a pre-encoded credential blob from the environment, the config should accept a single opaque env var rather than concatenating two separate env vars.

Consider keeping username and password as separate config fields. If base64-encoded passwords are required, decode at the point of use with clear documentation of the expectation.

Suggested approach
     postgres: Object.freeze({
         host: env['postgres_host'] || "localhost",
         port: env['postgres_port'] || 5432,
         database: env['postgres_database'] || "obsrv",
-        credentials: `${env['postgres_username'] || "postgres"}::${env['postgres_password'] || "postgres"}`
+        username: env['postgres_username'] || "postgres",
+        password: env['postgres_password'] || "postgres",
     }),

Then in databaseConnection.ts, if the password env var is expected to be base64-encoded:

const password = Buffer.from(connectionConfig.postgres.password, 'base64').toString('utf-8');
🤖 Prompt for AI Agents
In `@api-service/src/configs/ConnectionsConfig.ts` around lines 5 - 10, The
postgres config currently exposes a single concatenated credentials string
(postgres.credentials) which relies on a fragile "::" separator and mismatched
base64 assumptions; change ConnectionsConfig to expose separate fields
postgres.username and postgres.password (avoid concatenation), set defaults as
plain strings (e.g., "postgres"), and update the consumer (databaseConnection.ts
which currently does Buffer.from(credentials[1], 'base64')) to read
connectionConfig.postgres.password and perform base64 decoding there only if the
environment contract requires it; ensure you remove any split("::") usage and
handle decoding explicitly (Buffer.from(password, 'base64').toString('utf-8'))
or skip decoding if the password is plaintext.

Comment on lines +4 to +7
const { database, host, port } = connectionConfig.postgres;
const credentials = connectionConfig.postgres.credentials.split("::");

export const sequelize = new Sequelize({
database, password, username: username, dialect: "postgres", host, port: +port, pool: {
max: 2,
min: 1,
acquire: 30000,
idle: 10000
}
})
const decodedCredentials = Buffer.from(credentials[1], 'base64').toString('utf-8');
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

No validation on credential parsing — app will crash with an unhelpful error on misconfiguration.

If the credentials string from config doesn't contain "::", credentials[1] will be undefined, and Buffer.from(undefined, 'base64') will throw a TypeError at module load time, crashing the application with no indication that the credential format is wrong.

At minimum, add a guard:

Suggested fix
 const credentials = connectionConfig.postgres.credentials.split("::");
+if (credentials.length < 2 || !credentials[1]) {
+    throw new Error("Invalid postgres credentials format. Expected 'username::base64password'.");
+}
 const decodedCredentials = Buffer.from(credentials[1], 'base64').toString('utf-8');

However, as noted in the ConnectionsConfig.ts review, the entire credential-string approach is fragile. Separate username and password fields would eliminate this class of errors.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { database, host, port } = connectionConfig.postgres;
const credentials = connectionConfig.postgres.credentials.split("::");
export const sequelize = new Sequelize({
database, password, username: username, dialect: "postgres", host, port: +port, pool: {
max: 2,
min: 1,
acquire: 30000,
idle: 10000
}
})
const decodedCredentials = Buffer.from(credentials[1], 'base64').toString('utf-8');
const { database, host, port } = connectionConfig.postgres;
const credentials = connectionConfig.postgres.credentials.split("::");
if (credentials.length < 2 || !credentials[1]) {
throw new Error("Invalid postgres credentials format. Expected 'username::base64password'.");
}
const decodedCredentials = Buffer.from(credentials[1], 'base64').toString('utf-8');
🤖 Prompt for AI Agents
In `@api-service/src/connections/databaseConnection.ts` around lines 4 - 7, Guard
the fragile credentials parsing in databaseConnection by validating the split
result before decoding: check connectionConfig.postgres.credentials contains
"::" and that credentials.length >= 2 and credentials[1] is a non-empty string,
then attempt Buffer.from(...) inside a try/catch to surface a clear, actionable
error (or rethrow) if base64 decoding fails; update the variables referenced
(credentials and decodedCredentials) so they aren’t computed from undefined and
add a descriptive error message that mentions
connectionConfig.postgres.credentials format expectations and suggests using
separate username/password fields.

const { url, method, headers = {}, body = {}, params = {}, ...rest } = query;
const apiResponse = await axios.request({ url, method, headers, params, data: body, ...rest })
const data = _.get(apiResponse, "data");
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Header casing differs from other files in this PR.

This file uses 'Strict-Transport-Security' (canonical casing) while ResponseHandler.ts and prometheus/index.ts use 'STRICT-TRANSPORT-SECURITY'. Standardize to the canonical form across all files.

🤖 Prompt for AI Agents
In `@api-service/src/controllers/DataMetrics/DataMetricsController.ts` at line 29,
Standardize the Strict-Transport-Security header name to the canonical casing:
update the header string usages (e.g., the res.setHeader call in
DataMetricsController.ts) so all files use 'Strict-Transport-Security' (not
'STRICT-TRANSPORT-SECURITY'); locate occurrences in ResponseHandler.ts and
prometheus/index.ts (search for setHeader or the header key constant) and
replace the uppercase variant with the canonical 'Strict-Transport-Security'
string so every module uses the same header name.

const listHandler = async (request: Request, response: Response, next: NextFunction) => {
try {
const { limit, filters, offset } = request.body?.request || {};
const { limit, filters, offset } = _.get(request.body, 'request', {});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent: request.body is accessed directly here while all other handlers use _.get(request, "body").

If the goal is safe property access, this line should follow the same pattern:

-        const { limit, filters, offset } = _.get(request.body, 'request', {});
+        const { limit, filters, offset } = _.get(request, 'body.request', {});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { limit, filters, offset } = _.get(request.body, 'request', {});
const { limit, filters, offset } = _.get(request, 'body.request', {});
🤖 Prompt for AI Agents
In `@api-service/src/controllers/NotificationChannel/Notification.ts` at line 50,
The handler in Notification.ts uses _.get(request.body, 'request', {}) which
inconsistently accesses request.body directly; change it to use safe lodash
access consistent with other handlers—e.g., retrieve the body via _.get(request,
'body', {}) and then extract the nested request object (or use _.get(request,
'body.request', {})) so the destructuring of limit, filters, offset uses the
same safe access pattern as other handlers.

try {
res.set("Content-Type", register.contentType);
const metrics = await register.metrics()
res.setHeader('STRICT-TRANSPORT-SECURITY', 'max-age=31536000; includeSubDomains');
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Inconsistent header casing and duplicated HSTS logic across handlers.

Two issues:

  1. Casing: This file and ResponseHandler.ts use 'STRICT-TRANSPORT-SECURITY' (all-caps), while DataMetricsController.ts (line 29) uses 'Strict-Transport-Security'. HTTP headers are case-insensitive per RFC 7230, but the canonical form is Strict-Transport-Security. Standardize across the codebase.

  2. Duplication: The same res.setHeader(...) call is copy-pasted into every response path (ResponseHandler.successResponse, errorResponse, obsrvErrorResponse, flatResponse, goneResponse, metricsScrapeHandler, dataMetrics). This is fragile — any new response path will silently miss the header. A single Express middleware would be simpler and more reliable:

app.use((req, res, next) => {
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  next();
});
🤖 Prompt for AI Agents
In `@api-service/src/metrics/prometheus/index.ts` at line 28, Standardize the HSTS
header name and centralize its setting: replace all occurrences of the all-caps
header string with the canonical "Strict-Transport-Security" and remove the
duplicated res.setHeader calls inside ResponseHandler.successResponse,
ResponseHandler.errorResponse, ResponseHandler.obsrvErrorResponse,
ResponseHandler.flatResponse, ResponseHandler.goneResponse, metricsScrapeHandler
and dataMetrics; instead add a single Express middleware mounted early (e.g.,
via app.use) that calls res.setHeader('Strict-Transport-Security',
'max-age=31536000; includeSubDomains') and calls next() so every response
automatically gets the header.

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