Skip to content

EPMRPP-114318 || Add client-side validations and truncation#261

Open
maria-hambardzumian wants to merge 2 commits intodevelopfrom
feature/EPMRPP-114318-Add-client-side-validations-and-truncation
Open

EPMRPP-114318 || Add client-side validations and truncation#261
maria-hambardzumian wants to merge 2 commits intodevelopfrom
feature/EPMRPP-114318-Add-client-side-validations-and-truncation

Conversation

@maria-hambardzumian
Copy link
Copy Markdown
Contributor

@maria-hambardzumian maria-hambardzumian commented Apr 19, 2026

https://jiraeu.epam.com/browse/EPMRPP-114318

Summary by CodeRabbit

  • New Features

    • New configuration options to control data sanitization and truncation for launches and test items.
    • Automatic removal of binary control characters from submissions.
    • Configurable length limits for names, descriptions, and attributes with automatic truncation.
  • Tests

    • Added comprehensive test coverage for sanitization and truncation features.

@maria-hambardzumian
Copy link
Copy Markdown
Contributor Author

maria-hambardzumian commented Apr 19, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

Warning

Rate limit exceeded

@maria-hambardzumian has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 15 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 52 minutes and 15 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 436c68b1-09d7-42b1-ad48-30065510c663

📥 Commits

Reviewing files that changed from the base of the PR and between 3471064 and acddab9.

📒 Files selected for processing (2)
  • __tests__/helpers.spec.js
  • lib/helpers.js

Walkthrough

The pull request adds comprehensive sanitization and truncation functionality to the ReportPortal client. New helper functions process launch and test item metadata—removing binary control characters and enforcing length limits on names, descriptions, and attributes. Configuration options control these behaviors, with sensible defaults and explicit length limits exported as constants.

Changes

Cohort / File(s) Summary
Test Coverage
__tests__/helpers.spec.js, __tests__/report-portal-client.spec.js
Added test suites for helper functions and client sanitization/truncation integration, covering binary character replacement, field truncation with ellipsis suffix, attribute filtering/sorting, and configuration toggles.
Type Definitions
index.d.ts
Extended ReportPortalConfig interface with seven optional properties for truncation toggles (truncateAttributes, truncateFields, replaceBinaryChars) and length limits for launch/item names and descriptions.
Constants & Helpers
lib/constants/limits.js, lib/helpers.js
Added limit constants (attribute length/count, text field lengths) and implemented core sanitization functions: cleanBinaryCharacters, truncateString, sanitizeField, and truncateAttributes with conditional behavior based on options.
Configuration
lib/commons/config.js
Extended getClientConfig() to initialize new truncation flags (defaulting to true) and length limit options (using imported constants as defaults).
Client Integration
lib/report-portal-client.js
Added sanitizeData() method and integrated sanitization into launch/test-item start/finish/update flows, applying appropriate name and description length limits at each stage.

Sequence Diagram

sequenceDiagram
    actor Client
    participant RPClient as ReportPortal Client
    participant Sanitizer as Sanitizer/Helpers
    participant ReportPortal as ReportPortal API

    Client->>RPClient: startLaunch(launchData)
    RPClient->>Sanitizer: sanitizeData(launchData, limits)
    Sanitizer->>Sanitizer: cleanBinaryCharacters(name)
    Sanitizer->>Sanitizer: truncateString(name, 256)
    Sanitizer->>Sanitizer: truncateAttributes(attributes)
    Sanitizer-->>RPClient: sanitizedData
    RPClient->>ReportPortal: POST /api/v2/launch
    ReportPortal-->>RPClient: launchResponse

    Client->>RPClient: startTestItem(itemData)
    RPClient->>Sanitizer: sanitizeData(itemData, limits)
    Sanitizer->>Sanitizer: cleanBinaryCharacters(name)
    Sanitizer->>Sanitizer: truncateString(name, 1024)
    Sanitizer->>Sanitizer: truncateAttributes(attributes)
    Sanitizer-->>RPClient: sanitizedData
    RPClient->>ReportPortal: POST /api/v2/item
    ReportPortal-->>RPClient: itemResponse
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Whiskers twitching with delight,
Binary shadows vanish in the night,
Names and descriptions trimmed just right,
With ellipsis marks—oh, what a sight!
Attributes sorted, 256 strong,
Sanitized data marches along! 🌟

🚥 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 'EPMRPP-114318 || Add client-side validations and truncation' directly and clearly summarizes the main change: adding client-side validations and truncation functionality, which is confirmed by the changeset introducing sanitization helpers, configuration options, and truncation logic across the codebase.
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/EPMRPP-114318-Add-client-side-validations-and-truncation

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
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (1)
lib/report-portal-client.js (1)

714-714: ⚠️ Potential issue | 🟡 Minor

Debug log shows pre-sanitization payload.

This logDebug emits finishTestItemRQ (the caller's raw input) even though the actual request is built from finishTestItemData. Users debugging truncation/sanitization issues will see misleading output. Swap in the sanitized object:

-        this.logDebug(`Finish test item with tempId ${itemTempId}`, finishTestItemRQ);
+        this.logDebug(`Finish test item with tempId ${itemTempId}`, finishTestItemData);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/report-portal-client.js` at line 714, The debug log is printing the
caller's raw payload (finishTestItemRQ) instead of the sanitized request
actually sent; update the this.logDebug call in the finish-test-item flow to log
the sanitized object (finishTestItemData) instead of finishTestItemRQ so
debugging reflects the real outgoing payload—i.e., replace the second argument
to this.logDebug(`Finish test item with tempId ${itemTempId}`, ...) with
finishTestItemData (keeping the same message and itemTempId variable).
🧹 Nitpick comments (5)
lib/helpers.js (2)

56-61: truncateAttributeFields unconditionally coerces value even when key-only is present.

result.value = truncateString(String(result.value), ...) turns an undefined/missing value into the literal string "undefined". In practice truncateAttributes filters out entries without a value before calling this (line 82), so it's safe today — but if that filter is ever loosened, this becomes a data-corruption bug. Consider mirroring the if (result.key) guard for symmetry and defensiveness:

Proposed fix
-  if (result.key) result.key = truncateString(String(result.key), ATTRIBUTE_LENGTH_LIMIT);
-  result.value = truncateString(String(result.value), ATTRIBUTE_LENGTH_LIMIT);
+  if (result.key != null) result.key = truncateString(String(result.key), ATTRIBUTE_LENGTH_LIMIT);
+  if (result.value != null) result.value = truncateString(String(result.value), ATTRIBUTE_LENGTH_LIMIT);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/helpers.js` around lines 56 - 61, The function truncateAttributeFields
currently coerces result.value unconditionally which turns undefined into the
string "undefined"; update truncateAttributeFields to only call truncateString
on result.value when it is present (mirror the existing if (result.key) guard),
i.e., check for result.value (or result.hasOwnProperty('value') / result.value
!= null) before assigning result.value = truncateString(String(result.value),
ATTRIBUTE_LENGTH_LIMIT), using the same ATTRIBUTE_LENGTH_LIMIT and
truncateString helper.

34-42: sanitizeField assumes a string value; non-string inputs will throw.

If a caller passes a number/boolean as name or description (not impossible given the JS public API), cleanBinaryCharacters calls .replace on it and throws. Either coerce with String(value) at the top or document the contract:

-  if (!value) return value;
-
-  let result = value;
+  if (value == null || value === '') return value;
+  let result = String(value);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/helpers.js` around lines 34 - 42, sanitizeField assumes its input is a
string and will throw if given non-strings because cleanBinaryCharacters and
truncateString call string methods; update sanitizeField to coerce
non-null/undefined inputs to strings (e.g., call String(value) at start) before
calling cleanBinaryCharacters/truncateString, or alternatively validate and
early-return for non-string types; reference sanitizeField,
cleanBinaryCharacters and truncateString when making the change so callers
supplying numbers/booleans won’t crash.
lib/commons/config.js (1)

128-133: Explicit 0 limit silently becomes the default.

options.launchNameLengthLimit || LAUNCH_NAME_LENGTH_LIMIT (and the three siblings) treats 0 as "unset" and falls back to the default. If a user deliberately passes 0 (or any other falsy numeric), they'll be surprised. Prefer an explicit undefined check:

Proposed fix
-      launchNameLengthLimit: options.launchNameLengthLimit || LAUNCH_NAME_LENGTH_LIMIT,
-      itemNameLengthLimit: options.itemNameLengthLimit || ITEM_NAME_LENGTH_LIMIT,
-      launchDescriptionLengthLimit:
-        options.launchDescriptionLengthLimit || LAUNCH_DESCRIPTION_LENGTH_LIMIT,
-      itemDescriptionLengthLimit:
-        options.itemDescriptionLengthLimit || ITEM_DESCRIPTION_LENGTH_LIMIT,
+      launchNameLengthLimit:
+        options.launchNameLengthLimit ?? LAUNCH_NAME_LENGTH_LIMIT,
+      itemNameLengthLimit: options.itemNameLengthLimit ?? ITEM_NAME_LENGTH_LIMIT,
+      launchDescriptionLengthLimit:
+        options.launchDescriptionLengthLimit ?? LAUNCH_DESCRIPTION_LENGTH_LIMIT,
+      itemDescriptionLengthLimit:
+        options.itemDescriptionLengthLimit ?? ITEM_DESCRIPTION_LENGTH_LIMIT,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/commons/config.js` around lines 128 - 133, The current assignments for
launchNameLengthLimit, itemNameLengthLimit, launchDescriptionLengthLimit, and
itemDescriptionLengthLimit use the || operator so falsy numeric values like 0
fall back to defaults; change each to use an explicit undefined check on the
corresponding options property (e.g., options.launchNameLengthLimit !==
undefined ? options.launchNameLengthLimit : LAUNCH_NAME_LENGTH_LIMIT) so an
explicit 0 is preserved.
lib/report-portal-client.js (1)

81-97: sanitizeData mutates its input.

All current call sites already pass a freshly spread object, so this is safe today. Given the helper is named generically and may be reused, consider making it non-mutating to avoid future foot-guns:

Proposed fix
-  sanitizeData(data, { nameLimit, descriptionLimit } = {}) {
+  sanitizeData(data, { nameLimit, descriptionLimit } = {}) {
+    const result = { ...data };
     const opts = {
       replaceBinaryChars: this.config.replaceBinaryChars,
       truncateFields: this.config.truncateFields,
       truncateAttributes: this.config.truncateAttributes,
     };
-    if (nameLimit && data.name != null) {
-      data.name = helpers.sanitizeField(data.name, nameLimit, opts);
+    if (nameLimit && result.name != null) {
+      result.name = helpers.sanitizeField(result.name, nameLimit, opts);
     }
-    if (descriptionLimit && data.description != null) {
-      data.description = helpers.sanitizeField(data.description, descriptionLimit, opts);
+    if (descriptionLimit && result.description != null) {
+      result.description = helpers.sanitizeField(result.description, descriptionLimit, opts);
     }
-    if (data.attributes) {
-      data.attributes = helpers.truncateAttributes(data.attributes, opts);
+    if (result.attributes) {
+      result.attributes = helpers.truncateAttributes(result.attributes, opts);
     }
-    return data;
+    return result;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/report-portal-client.js` around lines 81 - 97, sanitizeData currently
mutates its input object; make it non-mutating by creating and operating on a
shallow copy at the start (e.g., const sanitized = { ...data }) and return that
copy instead of mutating `data`; when adjusting nested fields ensure you replace
properties on the copy (sanitized.name, sanitized.description) and assign
sanitized.attributes = helpers.truncateAttributes(...) (or a cloned/processed
attributes array) so helpers.sanitizeField and helpers.truncateAttributes are
applied to the copy while still using this.config.replaceBinaryChars /
truncateFields / truncateAttributes, then return the sanitized copy.
__tests__/helpers.spec.js (1)

160-168: Consider extending coverage to the full C0+DEL control range.

The "all purely binary control characters" test only probes 0x00-0x07 and 0x11-0x1B, which is exactly what the current regex matches — so regex gaps (e.g. \x08, \x0B, \x0C, \x0E-\x10, \x1C-\x1F, \x7F) go undetected. See the related comment on BINARY_CHARS in lib/helpers.js. Expanding this fixture would guard against regressions.

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

In `@__tests__/helpers.spec.js` around lines 160 - 168, Update the test for
helpers.cleanBinaryCharacters to cover the full C0 control range plus DEL so
regex regressions are caught: construct binaryCodes to include 0x00–0x1F and
0x7F but exclude the allowed whitespace controls (0x09, 0x0A, 0x0D), then build
input via String.fromCharCode and assert the result is '\uFFFD' repeated for
that full set; reference helpers.cleanBinaryCharacters and the BINARY_CHARS
behavior when updating the fixture.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/helpers.js`:
- Around line 63-83: truncateAttributes currently sorts the entire attributes
array and slices to ATTRIBUTE_NUMBER_LIMIT which can drop system attributes
appended later; change truncateAttributes to first partition attributes (use
isAttributeObject and attribute.system === true) into systemEntries and
userEntries, sort and apply compareAttributesByKey and the
ATTRIBUTE_NUMBER_LIMIT cap only to userEntries, then recombine systemEntries
(keeping them first or always included) with the capped userEntries before
applying replaceBinaryChars (cleanAttributeBinaryChars) and
truncateAttributeFields; ensure non-object attributes handling and the
truncateEnabled flag behavior remain unchanged.
- Around line 21-26: The BINARY_CHARS regex in lib/helpers.js is missing many
control characters (e.g. \x08, \x0B-\x0F, \x10, \x1C-\x1F, \x7F), so update the
BINARY_CHARS pattern used by cleanBinaryCharacters to match all C0 control bytes
except the common whitespace controls (TAB \x09, LF \x0A, CR \x0D) — i.e.
replace the current ranges with a single pattern that targets
\x00-\x08\x0B\x0C\x0E-\x1F\x7F (or equivalently whitelist only \t\n\r and
replace everything else in the 0x00-0x1F/0x7F set) and keep
cleanBinaryCharacters behavior of replacing matches with '\uFFFD'; also extend
the tests in __tests__/helpers.spec.js to assert removal/replacement for the
previously uncovered bytes (\x08, \x0B, \x0C, \x0E-\x1F, \x7F) so the gap is
covered.

---

Outside diff comments:
In `@lib/report-portal-client.js`:
- Line 714: The debug log is printing the caller's raw payload
(finishTestItemRQ) instead of the sanitized request actually sent; update the
this.logDebug call in the finish-test-item flow to log the sanitized object
(finishTestItemData) instead of finishTestItemRQ so debugging reflects the real
outgoing payload—i.e., replace the second argument to this.logDebug(`Finish test
item with tempId ${itemTempId}`, ...) with finishTestItemData (keeping the same
message and itemTempId variable).

---

Nitpick comments:
In `@__tests__/helpers.spec.js`:
- Around line 160-168: Update the test for helpers.cleanBinaryCharacters to
cover the full C0 control range plus DEL so regex regressions are caught:
construct binaryCodes to include 0x00–0x1F and 0x7F but exclude the allowed
whitespace controls (0x09, 0x0A, 0x0D), then build input via String.fromCharCode
and assert the result is '\uFFFD' repeated for that full set; reference
helpers.cleanBinaryCharacters and the BINARY_CHARS behavior when updating the
fixture.

In `@lib/commons/config.js`:
- Around line 128-133: The current assignments for launchNameLengthLimit,
itemNameLengthLimit, launchDescriptionLengthLimit, and
itemDescriptionLengthLimit use the || operator so falsy numeric values like 0
fall back to defaults; change each to use an explicit undefined check on the
corresponding options property (e.g., options.launchNameLengthLimit !==
undefined ? options.launchNameLengthLimit : LAUNCH_NAME_LENGTH_LIMIT) so an
explicit 0 is preserved.

In `@lib/helpers.js`:
- Around line 56-61: The function truncateAttributeFields currently coerces
result.value unconditionally which turns undefined into the string "undefined";
update truncateAttributeFields to only call truncateString on result.value when
it is present (mirror the existing if (result.key) guard), i.e., check for
result.value (or result.hasOwnProperty('value') / result.value != null) before
assigning result.value = truncateString(String(result.value),
ATTRIBUTE_LENGTH_LIMIT), using the same ATTRIBUTE_LENGTH_LIMIT and
truncateString helper.
- Around line 34-42: sanitizeField assumes its input is a string and will throw
if given non-strings because cleanBinaryCharacters and truncateString call
string methods; update sanitizeField to coerce non-null/undefined inputs to
strings (e.g., call String(value) at start) before calling
cleanBinaryCharacters/truncateString, or alternatively validate and early-return
for non-string types; reference sanitizeField, cleanBinaryCharacters and
truncateString when making the change so callers supplying numbers/booleans
won’t crash.

In `@lib/report-portal-client.js`:
- Around line 81-97: sanitizeData currently mutates its input object; make it
non-mutating by creating and operating on a shallow copy at the start (e.g.,
const sanitized = { ...data }) and return that copy instead of mutating `data`;
when adjusting nested fields ensure you replace properties on the copy
(sanitized.name, sanitized.description) and assign sanitized.attributes =
helpers.truncateAttributes(...) (or a cloned/processed attributes array) so
helpers.sanitizeField and helpers.truncateAttributes are applied to the copy
while still using this.config.replaceBinaryChars / truncateFields /
truncateAttributes, then return the sanitized copy.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7422e7f1-c78a-4528-9328-09774f64cb23

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6da92 and 3471064.

📒 Files selected for processing (7)
  • __tests__/helpers.spec.js
  • __tests__/report-portal-client.spec.js
  • index.d.ts
  • lib/commons/config.js
  • lib/constants/limits.js
  • lib/helpers.js
  • lib/report-portal-client.js

Comment thread lib/helpers.js Outdated
Comment thread lib/helpers.js
Comment on lines +63 to +83
const truncateAttributes = (attributes, options = {}) => {
const { replaceBinaryChars = true, truncateAttributes: truncateEnabled = true } = options;

if (!Array.isArray(attributes)) return attributes;

let result = attributes.filter(isAttributeObject).map((attr) => ({ ...attr }));
if (result.length === 0) return [];

if (result.length > ATTRIBUTE_NUMBER_LIMIT) {
result.sort(compareAttributesByKey);
result = result.slice(0, ATTRIBUTE_NUMBER_LIMIT);
}

if (replaceBinaryChars) {
result = result.map(cleanAttributeBinaryChars);
}

if (!truncateEnabled) return result;

return result.filter((attr) => attr.value != null).map(truncateAttributeFields);
};
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 | 🟡 Minor

Sort-then-cap can drop system attributes.

When a caller supplies >256 attributes, the list is sorted by key and sliced to 256. Since startLaunch appends system attributes (client, os, skippedIssue, etc.) after user attributes, those system attributes — which downstream consumers rely on for client/OS/version reporting — may be silently discarded depending on alphabetic ordering of user keys. Consider partitioning: always keep system: true entries (or keep them first) and apply the 256-cap only to non-system entries.

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

In `@lib/helpers.js` around lines 63 - 83, truncateAttributes currently sorts the
entire attributes array and slices to ATTRIBUTE_NUMBER_LIMIT which can drop
system attributes appended later; change truncateAttributes to first partition
attributes (use isAttributeObject and attribute.system === true) into
systemEntries and userEntries, sort and apply compareAttributesByKey and the
ATTRIBUTE_NUMBER_LIMIT cap only to userEntries, then recombine systemEntries
(keeping them first or always included) with the capped userEntries before
applying replaceBinaryChars (cleanAttributeBinaryChars) and
truncateAttributeFields; ensure non-object attributes handling and the
truncateEnabled flag behavior remain unchanged.

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