Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/json validation #1700

Merged
merged 11 commits into from
Feb 12, 2025

Conversation

YinDongFang
Copy link
Contributor

@YinDongFang YinDongFang commented Feb 6, 2025

Describe your changes

Support json validation and match method(regex, include, equal) by JSONPath

Test cases:
https://excalidraw.com/#json=2c9IgahseNFk4PNMeDiXL,5wEIYKEyOx0ly_0L4FACEA

Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks.

  • (Do not skip this or your PR will be closed) I deployed the application locally.
  • (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • The issue I am working on is assigned to me.
  • I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
  • I made sure font sizes, color choices etc are all referenced from the theme.
  • My PR is granular and targeted to one specific feature.
  • I took a screenshot or a video and attached to this PR if there is a UI change.

Copy link

coderabbitai bot commented Feb 6, 2025

Walkthrough

This pull request updates the monitor configuration on the server by adding three new fields—jsonPath, expectedValue, and matchMethod (with enum values "equal", "include", "regex")—to the Monitor schema and its Joi validations. The network service now extracts these fields from incoming job data, applying enhanced response validation with JSONPath handling. Additionally, a dependency on jmespath has been added to support the new JSONPath functionality in HTTP response processing.

Changes

File(s) Change Summary
Server/db/models/Monitor.js, Server/validation/joi.js Added new monitor configuration fields: jsonPath, expectedValue, and matchMethod (enum: "equal", "include", "regex") to the Monitor schema and corresponding Joi validations.
Server/service/networkService.js Imported jmespath, updated the requestHttp method to destructure additional job data, incorporate response validation using jsonPath and matchMethod, and refined the constructor by adding a stringService dependency.
Server/package.json Added new dependency: "jmespath": "^0.16.0".
Server/locales/en.json Introduced new error messages for HTTP responses and re-added the entry for "statusPageByTeamId" with the same message. Added entries for handling various HTTP-related scenarios.
Server/service/stringService.js Added new getter methods to retrieve translations for various HTTP-related messages, enhancing the StringService class functionality.

Sequence Diagram(s)

sequenceDiagram
    participant Job as Job Data
    participant NS as NetworkService
    participant HTTP as HTTP Service
    participant Validator as Response Validator

    Job->>NS: Submit job with monitor details (url, jsonPath, expectedValue, matchMethod)
    NS->>HTTP: Execute HTTP request using provided URL
    HTTP-->>NS: Return HTTP response
    alt expectedValue provided
      NS->>Validator: Extract value using jsonPath
      Validator-->>NS: Return extracted value
      NS->>NS: Validate extracted value using matchMethod vs expectedValue
    end
    NS-->>Job: Return response status and message
Loading

Possibly related PRs

  • Fix validation error of container id #1681: The changes in the main PR, which involve adding new fields to the MonitorSchema, are related to the modifications in the validation schemas in the retrieved PR, as both involve the jsonPath, expectedValue, and matchMethod fields.
  • Create status page PR1 #1493: The changes in the main PR are related to the modifications in the NetworkService class, specifically regarding the handling of jsonPath, expectedValue, and matchMethod, which are also introduced in the main PR's MonitorSchema.
  • feat: be/distributed uptime #1695: The changes in the main PR, which add fields to the MonitorSchema, are directly related to the modifications in the retrieved PR that enhance the DistributedUptimeController and introduce new functionality for handling monitors, as they both involve the Monitor model and its associated data structures.

Suggested reviewers

  • ajhollid: Suggested as a reviewer for insights on the schema and validation changes.
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai 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:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

Documentation and Community

  • 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.

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

🧹 Nitpick comments (3)
Server/service/networkService.js (2)

126-126: Enhance readability with explicit conditional

Using && for conditional assignment can be less clear to readers. Consider using an explicit if statement for better readability and maintainability.

Apply this diff:

-			secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
+			if (secret !== undefined) {
+				config.headers = { Authorization: `Bearer ${secret}` };
+			}

164-164: Simplify content type check with optional chaining

You can make the content type check more concise using optional chaining.

Apply this diff:

-    					if (contentType && contentType.includes('application/json')) {
+    					if (contentType?.includes('application/json')) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 164-164: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Server/db/models/Monitor.js (1)

41-54: Consider adding validation constraints to new schema fields

The fields jsonPath, expectedValue, and matchMethod currently lack validation constraints. To ensure data integrity, consider adding validations such as required fields or default values.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 547f97f and 399184b.

⛔ Files ignored due to path filters (1)
  • Server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • Server/db/models/Monitor.js (1 hunks)
  • Server/package.json (1 hunks)
  • Server/service/networkService.js (3 hunks)
  • Server/validation/joi.js (2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
Server/service/networkService.js

[error] 164-164: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (4)
Server/service/networkService.js (3)

133-136: Inclusion of additional response fields is appropriate

Adding monitorId, teamId, and type to the httpResponse object enhances the context and is a good practice.


1-1: ⚠️ Potential issue

Verify the use of jmespath for JSONPath functionality

It appears that jmespath is imported to handle JSONPath queries. However, jmespath and JSONPath are distinct query languages with different syntax. Please verify that jmespath meets your requirements, or consider using a library like jsonpath that specifically supports JSONPath.


161-172: ⚠️ Potential issue

Ensure compatibility between jsonPath and jmespath

You're using jmespath.search() with a jsonPath expression. Since jmespath and JSONPath have different syntaxes, verify that the jsonPath provided is compatible with jmespath, or adjust the implementation accordingly.

🧰 Tools
🪛 Biome (1.9.4)

[error] 164-164: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Server/package.json (1)

29-29: Addition of "jmespath" Dependency

The inclusion of "jmespath": "^0.16.0" supports the new JSONPath functionality needed for JSON validation as described in the PR objectives. This change aligns with the overall goal of enhancing JSON processing. Keep an eye on the package’s release notes to ensure there aren’t any emerging vulnerabilities that could make your palms sweaty, just like those mom’s spaghetti moments!

Comment on lines 186 to 187
result = result.toString();
if (matchMethod === "include") match = result.includes(expectedValue);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve result string conversion for accurate matching

Using result.toString() might not yield meaningful results if result is an object. Consider using JSON.stringify(result) to convert objects to a JSON string for accurate comparison.

Apply this diff:

-    				result = result.toString();
+    				result = typeof result === 'object' ? JSON.stringify(result) : result.toString();
📝 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
result = result.toString();
if (matchMethod === "include") match = result.includes(expectedValue);
result = typeof result === 'object' ? JSON.stringify(result) : result.toString();
if (matchMethod === "include") match = result.includes(expectedValue);

Comment on lines +208 to +210
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add enumeration validation for matchMethod in edit monitor validation

Ensure consistency by adding the same enumeration constraints to matchMethod in the edit validation schema.

Apply this diff:

      matchMethod: joi.string(),
+     matchMethod: joi.string().valid('equal', 'include', 'regex'),

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

same here, worth addding this to the validation

Comment on lines +197 to +199
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add enumeration validation for matchMethod in create monitor validation

To ensure only valid match methods are accepted, add enumeration constraints to the matchMethod field.

Apply this diff:

      matchMethod: joi.string(),
+     matchMethod: joi.string().valid('equal', 'include', 'regex'),

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is worth doing

Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

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

Auto Pull Request Review from LlamaPReview

1. Overview

1.1 PR Summary

  • Business value and requirements alignment: This PR introduces JSON validation and matching capabilities using JSONPath, enhancing the HTTP monitoring functionality of the Checkmate tool. This aligns with the need for more robust and flexible monitoring features.
  • Key components modified:
    • Server/db/models/Monitor.js: Added fields for JSON validation.
    • Server/service/networkService.js: Implemented JSON validation logic.
    • Server/validation/joi.js: Updated validation schemas.
    • Server/package.json: Added jmespath dependency.
  • Impact assessment: The changes significantly enhance HTTP monitoring by allowing JSON response validation based on JSONPath expressions. This will improve the tool's ability to detect and respond to specific conditions in monitored endpoints.
  • System dependencies and integration impacts: The introduction of jmespath for JSONPath processing adds a new dependency and potential performance considerations. The removal of the distributed_http monitor type requires careful review to ensure no regression in functionality.

1.2 Architecture Changes

  • System design modifications: The PR extends the Monitor model to include fields for JSONPath, expected value, and match method. It modifies the networkService to incorporate JSON validation logic within HTTP request processing.
  • Component interactions: The networkService now interacts with the jmespath library to process JSONPath expressions and validate HTTP responses.
  • Integration points: The JSON validation logic is integrated into the existing HTTP monitoring workflow, affecting how HTTP responses are processed and validated.

2. Detailed Technical Analysis

2.1 Code Logic Deep-Dive

Core Logic Changes

  • Server/service/networkService.js - requestHttp
    • Submitted PR Code:
    			if (expectedValue) {
    				// validate if response data match expected value
    				let result = response?.data;

    				this.logger.info({
    					service: this.SERVICE_NAME,
    					method: "requestHttp",
    					message: `Job: [${name}](${_id}) match result with expected value`,
    					details: { expectedValue, result, jsonPath, matchMethod }
    				});

    				if (jsonPath) {
    					const contentType = response.headers['content-type'];

    					if (contentType && contentType.includes('application/json')) {
    						try {
    							result = jmespath.search(result, jsonPath);
    						} catch (error) {
    							httpResponse.status = false;
    							httpResponse.message = "JSONPath Search Error";
    							return httpResponse;
    						}
    					} else {
    						httpResponse.status = false;
    						httpResponse.message = "Response Not JSON";
    						return httpResponse;
    					}
    				}

    				if (result === null || result === undefined) {
    					httpResponse.status = false;
    					httpResponse.message = "Empty Result";
    					return httpResponse;
    				}

    				let match;
    				result = result.toString();
    				if (matchMethod === "include") match = result.includes(expectedValue);
    				else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
    				else match = result === expectedValue;

    				httpResponse.status = match;
    				httpResponse.message = match ? "Match" : "Not Match";
    				return httpResponse;
    			}
  • Analysis:
    • The current logic checks for expectedValue first. If it exists, it proceeds with validation. However, if expectedValue is provided but the response is not JSON and jsonPath is not provided, the code still attempts to validate response?.data. This could lead to unexpected behavior because response?.data might be undefined or not a stringifiable value in non-JSON responses, yet result.toString() is unconditionally called before matching.
    • Furthermore, the code assumes that if jsonPath is not provided, the entire response?.data should be treated as the value to be matched. This might be valid for simple cases, but it's not explicitly documented or handled robustly for non-JSON responses.
    • The toString() conversion of result before matching might not be appropriate for all types of data extracted by JSONPath. For example, if JSONPath extracts a number or boolean, converting it to a string for comparison might lead to type mismatch issues or incorrect matching behavior, especially for "equal" match method.
  • LlamaPReview Suggested Improvements:
    			if (expectedValue) {
    				// validate if response data match expected value
    				let result = response?.data;

    				this.logger.info({
    					service: this.SERVICE_NAME,
    					method: "requestHttp",
    					message: `Job: [${name}](${_id}) match result with expected value`,
    					details: { expectedValue, result, jsonPath, matchMethod }
    				});

    				if (jsonPath) {
    					const contentType = response.headers['content-type'];

    					if (contentType && contentType.includes('application/json')) {
    						try {
    							result = jmespath.search(result, jsonPath);
    						} catch (error) {
    							httpResponse.status = false;
    							httpResponse.message = "JSONPath Search Error";
    							return httpResponse;
    						}
    					} else {
    						httpResponse.status = false;
    						httpResponse.message = "Response Not JSON";
    						return httpResponse;
    					}
    				} else if (response && response.headers && response.headers['content-type'] && !response.headers['content-type'].includes('application/json')) {
    					// Handle non-JSON response when jsonPath is not provided but expectedValue is.
    					result = response.data; // Or response.data.toString() if you expect string conversion for non-JSON
    				}

    				if (result === null || result === undefined) {
    					httpResponse.status = false;
    					httpResponse.message = "Empty Result";
    					return httpResponse;
    				}

    				let match;
    				const resultValue = typeof result === 'string' ? result : String(result); // Ensure string conversion for matching, but be mindful of potential issues. Consider type-aware comparison if needed.
    				if (matchMethod === "include") match = resultValue.includes(expectedValue);
    				else if (matchMethod === "regex") match = new RegExp(expectedValue).test(resultValue);
    				else match = resultValue === expectedValue;

    				httpResponse.status = match;
    				httpResponse.message = match ? "Match" : "Not Match";
    				return httpResponse;
    			}
  • Improvement rationale:
    • Technical benefits: Addresses potential issues with non-JSON responses when expectedValue is provided without jsonPath. Adds explicit handling for non-JSON content when jsonPath is absent, making the logic more robust. The resultValue ensures string conversion for comparison while acknowledging potential type-related issues and suggesting consideration for type-aware comparisons if needed for future enhancements.
    • Business value: Improves the reliability of HTTP monitoring by handling a wider range of response types and validation scenarios, reducing false negatives or positives.
    • Risk assessment: Reduces the risk of unexpected errors or incorrect validation results when dealing with different content types. By explicitly handling non-JSON responses, the system becomes more predictable and maintainable.

Core Logic Changes

  • Server/service/networkService.js - requestHttp
    • Submitted PR Code:
    				if (result === null || result === undefined) {
    					httpResponse.status = false;
    					httpResponse.message = "Empty Result";
    					return httpResponse;
    				}

    				let match;
    				result = result.toString();
    				if (matchMethod === "include") match = result.includes(expectedValue);
    				else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
    				else match = result === expectedValue;

    				httpResponse.status = match;
    				httpResponse.message = match ? "Match" : "Not Match";
    				return httpResponse;
    			}
  • Analysis:
    • The current logic uses a simple string conversion result.toString() for all match methods ("include", "regex", "equal"). This might lead to incorrect comparisons, especially for non-string expectedValue. For instance, if expectedValue is intended to be a number but is passed as a string, the "equal" comparison will be string-based, not number-based. This is a potential logical error.
    • Furthermore, the regex match new RegExp(expectedValue).test(result) is vulnerable to regular expression injection if expectedValue is not properly sanitized. Although the context is internal monitoring configuration, it's still a good practice to consider sanitization or use safer regex matching methods if external input is ever incorporated into expectedValue. The initial review already mentioned JSONPath injection, but regex injection is another related security concern introduced by the matchMethod: regex.
  • LlamaPReview Suggested Improvements:
    				if (result === null || result === undefined) {
    					httpResponse.status = false;
    					httpResponse.message = "Empty Result";
    					return httpResponse;
    				}

    				let match;
    				let resultValue = result; // Keep original type for 'equal' comparison

    				if (matchMethod === "include") {
    					resultValue = String(result); // Convert to string only for 'include' and 'regex' if needed
    					match = resultValue.includes(expectedValue);
    				} else if (matchMethod === "regex") {
    					resultValue = String(result); // Convert to string for regex test
    					try {
    						match = new RegExp(expectedValue).test(resultValue); // Consider sanitizing expectedValue for regex injection
    					} catch (regexError) {
    						httpResponse.status = false;
    						httpResponse.message = "Regex Compilation Error"; // Handle invalid regex patterns
    						return httpResponse;
    					}
    				} else { // matchMethod === "equal"
    					match = resultValue == expectedValue; // Use loose equality for 'equal' to handle type coercion if needed, or strict equality '===' for type-sensitive comparison. Original code used strict equality after toString() conversion, which is not ideal.
    				}

    				httpResponse.status = match;
    				httpResponse.message = match ? "Match" : "Not Match";
    				return httpResponse;
    			}
  • Improvement rationale:
    • Technical benefits: Improves the accuracy of matching by preserving the original type of result for "equal" comparison and only converting to string when necessary for "include" and "regex". Adds error handling for regex compilation to prevent crashes due to invalid regex patterns. Addresses potential regex injection by highlighting the need for sanitization.
    • Business value: Ensures more reliable and accurate monitoring results, reducing false alarms caused by incorrect type comparisons or regex errors.
    • Risk assessment: Mitigates the risk of incorrect matching due to type mismatches and adds robustness against invalid regex patterns. Raises awareness of regex injection vulnerability, prompting further security considerations. The use of try-catch for regex compilation is a crucial improvement for error handling. Using loose equality == for "equal" match is a design choice to allow for type coercion, which might be beneficial in some use cases, but strict equality === could also be considered for more type-sensitive comparisons, depending on the desired behavior.

2.2 Implementation Quality

  • Code organization and structure: The PR is well-organized, with changes localized to relevant files. The addition of new fields and validation logic is clear and follows existing patterns.
  • Design patterns usage: The use of JSONPath for validation is a good design choice, leveraging a well-known standard for querying JSON data.
  • Error handling approach: The PR includes basic error handling for JSONPath search errors and empty results. However, additional error handling for regex compilation errors and non-JSON responses is suggested.
  • Resource management: The PR does not introduce significant new resources but adds a dependency on jmespath, which should be managed carefully.

3. Critical Findings

3.1 Potential Issues

  • 🔴 Critical Issues

    • Potential JSONPath injection vulnerability: The jsonPath input should be properly sanitized to prevent JSONPath injection attacks.
      • Impact: Could lead to security vulnerabilities if jsonPath is not properly sanitized.
      • Recommendation: Implement sanitization for jsonPath input to prevent injection attacks.
    • Performance impact of jmespath processing: The use of jmespath for JSONPath processing may introduce performance overhead, especially with complex paths and large JSON payloads.
      • Impact: Could degrade the performance of the monitoring service.
      • Recommendation: Assess jmespath performance under load and implement performance tests if necessary.
    • Increased complexity in requestHttp function: The added complexity in the requestHttp function may introduce bugs and maintainability challenges.
      • Impact: Increased risk of bugs and difficulties in maintaining the code.
      • Recommendation: Ensure thorough testing and consider refactoring for better maintainability.
    • Unintended removal of distributed_http functionality: The removal of the distributed_http monitor type could be a regression.
      • Impact: Loss of functionality for distributed HTTP monitoring.
      • Recommendation: Confirm if the removal is intentional and understand the rationale/impact.
  • 🟡 Warnings

    • Potential regex injection vulnerability: The regex match method is vulnerable to regular expression injection if expectedValue is not properly sanitized.
      • Potential risks: Could lead to security vulnerabilities if expectedValue is not properly sanitized.
      • Suggested improvements: Implement sanitization for expectedValue to prevent regex injection attacks.
    • Incorrect type comparisons: The current logic uses string conversion for all match methods, which might lead to incorrect comparisons.
      • Potential risks: Incorrect monitoring results due to type mismatches.
      • Suggested improvements: Preserve the original type of result for "equal" comparison and only convert to string when necessary for "include" and "regex".

3.2 Code Quality Concerns

  • Maintainability aspects: The added complexity in the requestHttp function may introduce maintainability challenges.
  • Readability issues: The current logic for matching methods could be clearer with better handling of different data types.
  • Performance bottlenecks: The use of jmespath for JSONPath processing may introduce performance overhead.

4. Security Assessment

  • Authentication/Authorization impacts: No direct impacts on authentication or authorization.
  • Data handling concerns: Potential JSONPath and regex injection vulnerabilities need careful review.
  • Input validation: Ensure proper sanitization of jsonPath and expectedValue inputs to prevent injection attacks.
  • Security best practices: Follow best practices for input validation and sanitization.
  • Potential security risks: JSONPath and regex injection vulnerabilities.
  • Mitigation strategies: Implement sanitization for jsonPath and expectedValue inputs.
  • Security testing requirements: Include tests for JSONPath and regex injection vulnerabilities.

5. Testing Strategy

5.1 Test Coverage

  • Unit test analysis: Ensure unit tests cover various JSONPath expressions, match methods, and error scenarios.
  • Integration test requirements: Implement integration tests for end-to-end JSON validation workflow.
  • Edge cases coverage: Include tests for non-JSON responses, empty results, and invalid regex patterns.

5.2 Test Recommendations

Suggested Test Cases

  // Example unit test for JSONPath validation
  it('should validate JSONPath and match expected value', async () => {
    const job = {
      data: {
        url: 'http://example.com',
        secret: 'secret',
        _id: '123',
        name: 'Test Job',
        teamId: 'team1',
        type: 'http',
        jsonPath: 'data.value',
        matchMethod: 'equal',
        expectedValue: 'expected'
      }
    };
    const response = {
      data: { data: { value: 'expected' } },
      headers: { 'content-type': 'application/json' }
    };
    const networkService = new NetworkService(axios, ping, logger, http, Docker, net);
    const result = await networkService.requestHttp(job, response);
    expect(result.status).toBe(true);
    expect(result.message).toBe('Match');
  });

  // Example unit test for regex match method
  it('should handle regex match method with sanitized input', async () => {
    const job = {
      data: {
        url: 'http://example.com',
        secret: 'secret',
        _id: '123',
        name: 'Test Job',
        teamId: 'team1',
        type: 'http',
        jsonPath: 'data.value',
        matchMethod: 'regex',
        expectedValue: '^ex.*$'
      }
    };
    const response = {
      data: { data: { value: 'expected' } },
      headers: { 'content-type': 'application/json' }
    };
    const networkService = new NetworkService(axios, ping, logger, http, Docker, net);
    const result = await networkService.requestHttp(job, response);
    expect(result.status).toBe(true);
    expect(result.message).toBe('Match');
  });
  • Coverage improvements: Ensure coverage for all match methods, JSONPath expressions, and error scenarios.
  • Performance testing needs: Assess jmespath performance under load and implement performance tests if necessary.

6. Documentation & Maintenance

  • Documentation updates needed: Update API and architecture documentation to reflect the new JSON validation capabilities.
  • Long-term maintenance considerations: Monitor the performance of jmespath processing and address any maintainability challenges in the requestHttp function.
  • Technical debt and monitoring requirements: Keep an eye on the complexity introduced by the new validation logic and address any technical debt that arises.

7. Deployment & Operations

  • Deployment impact and strategy: Ensure the new dependency jmespath is included in the deployment package. Monitor the performance of the monitoring service post-deployment.
  • Key operational considerations: Be aware of the potential performance impact of jmespath processing and monitor the system accordingly.

8. Summary & Recommendations

8.1 Key Action Items

  1. Implement sanitization for jsonPath and expectedValue inputs to prevent injection attacks.
  2. Assess jmespath performance under load and implement performance tests if necessary.
  3. Ensure thorough testing of the new validation logic, covering all match methods, JSONPath expressions, and error scenarios.
  4. Confirm if the removal of the distributed_http monitor type is intentional and understand the rationale/impact.

8.2 Future Considerations

  • Technical evolution path: Continuously monitor the performance and maintainability of the new validation logic.
  • Business capability evolution: Consider extending the JSON validation capabilities to other monitoring types if beneficial.
  • System integration impacts: Ensure the new validation logic integrates smoothly with existing monitoring workflows and does not introduce regressions.

💡 Help Shape LlamaPReview
How's this review format working for you? Vote in our Github Discussion Polls to help us improve your review experience!

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

🧹 Nitpick comments (3)
Server/service/networkService.js (3)

123-126: Enhance readability of authorization header logic.

The secret header logic can be more explicit and readable.

Consider this cleaner approach:

-    secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
+    if (secret) {
+      config.headers = { Authorization: `Bearer ${secret}` };
+    }

164-164: Strengthen content-type validation.

The current content-type check might be fragile for variations in MIME types.

Consider using a more robust check:

-    if (contentType && contentType.includes('application/json')) {
+    if (contentType?.toLowerCase().split(';')[0] === 'application/json') {
🧰 Tools
🪛 Biome (1.9.4)

[error] 164-164: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


185-193: Refactor match logic for better maintainability.

The match logic could be more structured using a strategy pattern.

Consider this approach:

-    let match;
-    result = typeof result === "object" ? JSON.stringify(result) : result.toString();
-    if (matchMethod === "include") match = result.includes(expectedValue);
-    else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
-    else match = result === expectedValue;
+    const matchers = {
+      include: (result, expected) => result.includes(expected),
+      regex: (result, expected) => new RegExp(expected).test(result),
+      equal: (result, expected) => result === expected
+    };
+    
+    result = typeof result === "object" ? JSON.stringify(result) : result.toString();
+    const matcher = matchers[matchMethod] || matchers.equal;
+    const match = matcher(result, expectedValue);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 399184b and 9399519.

📒 Files selected for processing (1)
  • Server/service/networkService.js (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
Server/service/networkService.js

[error] 164-164: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (2)
Server/service/networkService.js (2)

1-1: LGTM! Clean dependency addition and constructor update.

The jmespath import and Docker parameter addition look good.

Also applies to: 16-16


169-169: Enhance error messages and add type validation.

The error messages could be more descriptive and include validation for expected types.

Consider these improvements:

-            httpResponse.message = "JSONPath Search Error";
+            httpResponse.message = `JSONPath Search Error: Invalid path '${jsonPath}'`;

-            httpResponse.message = "Response Not JSON";
+            httpResponse.message = `Expected JSON response but got ${contentType}`;

-            httpResponse.message = "Empty Result";
+            httpResponse.message = `No data found at path '${jsonPath}'`;

Also applies to: 174-174, 181-181

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check for consistent error message patterns across the codebase
rg -A 1 'httpResponse\.message = "[^"]+";'

Length of output: 478


Action: Enhance error messages with dynamic details and add type validation

The shell script confirms that the error messages in Server/service/networkService.js are hard-coded and generic. These messages at lines 169, 174, and 181 would benefit from incorporating dynamic context—such as capturing the relevant JSON path or content type—and from adding type validation to ensure the response data meets expectations.

  • In line 169, replace:
    • From: httpResponse.message = "JSONPath Search Error";
    • To: httpResponse.message = \JSONPath Search Error: Invalid path '${jsonPath}'`;`
  • In line 174, replace:
    • From: httpResponse.message = "Response Not JSON";
    • To: httpResponse.message = \Expected JSON response but got ${contentType}`;`
  • In line 181, replace:
    • From: httpResponse.message = "Empty Result";
    • To: httpResponse.message = \No data found at path '${jsonPath}'`;`

These changes will provide more descriptive error details, making debugging easier. With the current evidence, please proceed with these improvements.

Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Hi @YinDongFang ,

Can you please give us some more details about this PR?

What is the feature/problem that is being implemented/solved?

The more details you can add the faster we can review 👍

@YinDongFang
Copy link
Contributor Author

Hi @YinDongFang ,

Can you please give us some more details about this PR?

What is the feature/problem that is being implemented/solved?

The more details you can add the faster we can review 👍

It's a alternative solution instead of solution in #1634 to implements feature requested in #1573. Supports response validation using JSONPath expressions with three matching strategies. Avoid security problem of executing user code.

🔑 Key Changes

  • Dependency
  • DB model (Monitor)
    • add jsonPath, expectedValue, matchMethod fields
  • NetworkSerivce
    • requestHttp function, add json validation logic code

🎬 Example Scene

The expected response data's message field contains "success"

// example response data expected
{
  message: 'request success!'
}

Then we can post monitor as:

{
  // ...other monitor fields
  jsonPath: 'message',
  matchMethod: 'include', // default is 'equal'
  expectedValue: 'success',
}

@gorkem-bwl
Copy link
Contributor

That sounds like a much cleaner / safer solution to me, @ajhollid

@ajhollid
Copy link
Collaborator

ajhollid commented Feb 7, 2025

Agreed, I think this is much safer for us.

I'll need a little bit of time to play with it but concept looks solid.

Thank you @YinDongFang 👍

Server/service/networkService.js Outdated Show resolved Hide resolved
Comment on lines +197 to +199
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is worth doing

Comment on lines +208 to +210
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here, worth addding this to the validation

Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Overall looks good to me. I think the nested if/else blocks can be simplified somewhat with early returns, so I think that is worth doing.

There isn't a front end implementation yet, are you planning on doing that?

else match = result === expectedValue;

httpResponse.status = match;
httpResponse.message = match ? "Match" : "Not Match";
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is an open PR for a localization module that will be added to the application probably sometime today, so after that merge is in please update this PR to use that module for user facing strings.

@gorkem-bwl
Copy link
Contributor

There isn't a front end implementation yet, are you planning on doing that?

I think there was one in the first implementation (sandboxed js run) as a PR, but it might have been closed. I remember seeing the screenshots.

@YinDongFang
Copy link
Contributor Author

Overall looks good to me. I think the nested if/else blocks can be simplified somewhat with early returns, so I think that is worth doing.

There isn't a front end implementation yet, are you planning on doing that?

Yes, I will implement the frontend

@ajhollid
Copy link
Collaborator

Overall looks good to me. I think the nested if/else blocks can be simplified somewhat with early returns, so I think that is worth doing.
There isn't a front end implementation yet, are you planning on doing that?

Yes, I will implement the frontend

Great, we can merge the back end implementation in first once the changes have been made and work on the front end in a new pull request. Thanks for your hard work!

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

🧹 Nitpick comments (3)
Server/service/networkService.js (3)

123-126: Simplify secret header configuration.

The current implementation can be made more concise using object shorthand.

Apply this diff:

-      secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
+      secret && (config.headers = { Authorization: `Bearer ${secret}` });

168-183: Enhance content-type check robustness.

The content-type check could be improved using optional chaining and case-insensitive comparison.

Apply this diff:

-      const contentType = response.headers['content-type'];
-      if (contentType && contentType.includes('application/json')) {
+      const contentType = response.headers?.['content-type']?.toLowerCase();
+      if (contentType?.includes('application/json')) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 170-170: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


191-195: Simplify match logic using a switch statement.

The current if-else chain can be made more maintainable using a switch statement.

Apply this diff:

-      let match;
-      result = typeof result === "object" ? JSON.stringify(result) : result.toString();
-      if (matchMethod === "include") match = result.includes(expectedValue);
-      else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
-      else match = result === expectedValue;
+      result = typeof result === "object" ? JSON.stringify(result) : result.toString();
+      const match = {
+        include: () => result.includes(expectedValue),
+        regex: () => new RegExp(expectedValue).test(result),
+        equal: () => result === expectedValue
+      }[matchMethod]?.() ?? false;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9399519 and fd1b153.

📒 Files selected for processing (1)
  • Server/service/networkService.js (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
Server/service/networkService.js

[error] 170-170: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (2)
Server/service/networkService.js (2)

1-1: LGTM! Clean dependency injection.

The addition of jmespath for JSON path validation and Docker parameter in the constructor follows good dependency injection practices.

Also applies to: 16-16


160-165: LGTM! Excellent logging implementation.

The logging implementation provides detailed context with structured data, making debugging easier.

@YinDongFang YinDongFang force-pushed the feature/json-validation branch from fd1b153 to 4d12608 Compare February 12, 2025 02:53
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)
Server/service/networkService.js (1)

198-198: ⚠️ Potential issue

Regex Pattern Validation Needs Enhancement

The current implementation directly creates a regular expression from user-supplied input without any restrictions, which could expose the system to ReDoS attacks. Best practices advise validating both the complexity and length of such patterns.

-    else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
+    else if (matchMethod === "regex") {
+        // Validate regex pattern complexity and length
+        if (expectedValue.length > 1000) {
+            httpResponse.status = false;
+            httpResponse.message = "Regex pattern too complex";
+            return httpResponse;
+        }
+        try {
+            match = new RegExp(expectedValue).test(result);
+        } catch (error) {
+            httpResponse.status = false;
+            httpResponse.message = "Invalid regex pattern";
+            return httpResponse;
+        }
+    }
🧹 Nitpick comments (2)
Server/service/networkService.js (2)

18-36: Add parameter validation in the constructor.

Yo! Let's make sure our constructor's got our back by validating those parameters—we don't want undefined dependencies causing trouble later!

 constructor(axios, ping, logger, http, Docker, net, stringService) {
+    if (!axios || !ping || !logger || !http || !Docker || !net || !stringService) {
+        throw new Error('All dependencies must be provided to NetworkService');
+    }
     this.TYPE_PING = "ping";
     // ... rest of the constructor

172-174: Improve content-type checking with optional chaining.

Let's make that content-type check smoother than mom's spaghetti!

-    const contentType = response.headers['content-type'];
-
-    if (contentType && contentType.includes('application/json')) {
+    if (response.headers?.['content-type']?.includes('application/json')) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 174-174: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd1b153 and 4d12608.

⛔ Files ignored due to path filters (1)
  • Server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • Server/db/models/Monitor.js (1 hunks)
  • Server/package.json (1 hunks)
  • Server/service/networkService.js (3 hunks)
  • Server/validation/joi.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • Server/package.json
  • Server/validation/joi.js
  • Server/db/models/Monitor.js
🧰 Additional context used
🪛 Biome (1.9.4)
Server/service/networkService.js

[error] 174-174: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +127 to +130
const { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data;
const config = {};

job.data.secret !== undefined &&
(config.headers = { Authorization: `Bearer ${job.data.secret}` });
secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance secret handling security.

The current secret handling might expose sensitive data in logs. Let's keep that secret sauce under wraps!

-    secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
+    if (secret) {
+        // Avoid using template literals with secrets to prevent accidental logging
+        config.headers = { Authorization: 'Bearer ' + secret };
+    }
📝 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 { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data;
const config = {};
job.data.secret !== undefined &&
(config.headers = { Authorization: `Bearer ${job.data.secret}` });
secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
const { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data;
const config = {};
if (secret) {
// Avoid using template literals with secrets to prevent accidental logging
config.headers = { Authorization: 'Bearer ' + secret };
}

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

🧹 Nitpick comments (1)
Server/service/networkService.js (1)

153-158: Refactor to use early returns for cleaner flow.

His palms are sweaty looking at these nested if/else blocks! Let's clean it up with early returns.

-    if (!expectedValue) {
-        // not configure expected value, return
-        httpResponse.status = true;
-        httpResponse.message = this.http.STATUS_CODES[response.status];
-        return httpResponse;
-    }
+    if (expectedValue === undefined) {
+        httpResponse.status = true;
+        httpResponse.message = this.http.STATUS_CODES[response.status];
+        return httpResponse;
+    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d12608 and 1f0876d.

📒 Files selected for processing (3)
  • Server/locales/en.json (1 hunks)
  • Server/service/networkService.js (3 hunks)
  • Server/service/stringService.js (1 hunks)
🔇 Additional comments (10)
Server/service/stringService.js (6)

324-326: LGTM! Yo, this getter's got the right flow!

The method follows the established pattern for translation getters, keeping it consistent with the rest of the class.


328-330: LGTM! This getter's straight outta translation heaven!

The method maintains the consistent pattern for translation getters, making it a clean addition to the class.


332-334: LGTM! This getter's serving up error messages like mom's spaghetti!

The method keeps the translation getter pattern consistent, making it a solid addition to the class.


336-338: LGTM! This getter's dropping empty results like they're hot!

The method maintains the translation getter pattern, keeping the class's style consistent.


340-342: LGTM! This getter's matching success like a rap battle champion!

The method follows the established translation getter pattern, maintaining class consistency.


344-346: LGTM! This getter's handling failures with style!

The method maintains the translation getter pattern, keeping it real with the class's consistency.

Server/service/networkService.js (3)

126-129: Enhance secret handling security.

The current secret handling might expose sensitive data in logs. Let's keep that secret sauce under wraps!

-    secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
+    if (secret) {
+        // Avoid using template literals with secrets to prevent accidental logging
+        config.headers = { Authorization: 'Bearer ' + secret };
+    }

189-196: Improve result string conversion for accurate matching.

Using result.toString() might not yield meaningful results if result is an object. Let's handle that spaghetti code!

-    result = typeof result === "object" ? JSON.stringify(result) : result.toString();
+    result = typeof result === 'object' ? JSON.stringify(result) : result.toString();

197-199: Regex Pattern Validation Needs Enhancement.

The current implementation directly creates a regular expression from user-supplied input without any restrictions, which could expose the system to ReDoS attacks. His knees weak, arms are heavy thinking about those potential DoS attacks!

Run this script to check for potential ReDoS vulnerabilities:

#!/bin/bash
# Description: Check for potential ReDoS vulnerabilities in regex patterns

# Test: Search for direct RegExp creation from user input
rg -A 5 'new RegExp\('
Server/locales/en.json (1)

147-153: LGTM! These translations are dropping bars like Eminem!

The translations are clear, concise, and properly formatted. They align perfectly with the HTTP response validation functionality.

@YinDongFang
Copy link
Contributor Author

@ajhollid I rebased the PR and use the localization service to display message. It looks good. Please review again.

image

@ajhollid
Copy link
Collaborator

@ajhollid I rebased the PR and use the localization service to display message. It looks good. Please review again.

image

Thanks @YinDongFang , I will review as soon as possible!

@gorkem-bwl
Copy link
Contributor

gorkem-bwl commented Feb 12, 2025

@ajhollid I rebased the PR and use the localization service to display message. It looks good. Please review again.

@YinDongFang May I use your Excalidraw drawing in our documentation for a reference guide when Alex accepts the PR for the next release?

@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Feb 12, 2025
Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

This looks good to me, thank you for your contribution @YinDongFang ! Much appreciated 👍

result = typeof result === "object" ? JSON.stringify(result) : result.toString();
if (matchMethod === "include") match = result.includes(expectedValue);
else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
else match = result === expectedValue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good, this is much easier to read 👍

@ajhollid ajhollid merged commit af6af73 into bluewave-labs:develop Feb 12, 2025
1 check passed
@YinDongFang
Copy link
Contributor Author

@YinDongFang May I use your Excalidraw drawing in our documentation for a reference guide when Alex accepts the PR for the next release?

Sure. You are welcome to use it

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.

3 participants