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

fix: add provider input validation & type safety #2430

Open
wants to merge 40 commits into
base: main
Choose a base branch
from

Conversation

theedigerati
Copy link

@theedigerati theedigerati commented Nov 11, 2024

Closes #1327

📑 Description

Validation for provider inputs using zod schema on the frontend and pydantic on the backend

  • zod schema for all provider inputs
  • custom validation logic for urls(with/without protocol), port, tld, file
  • built-in & custom pydantic fields types for inputs on all providers with such fields
  • type safety for provider form component
  • e2e tests

✅ Checks

  • My pull request adheres to the code style of this project
  • My code requires changes to the documentation
  • I have updated the documentation as required
  • All the tests have passed

ℹ Additional Information

https://www.loom.com/share/b08c408f192b4a29aef1d8f83c7c4638?sid=f21b2bb5-4390-4e3b-bd40-e966148597ed

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a comprehensive URL validation module using zod, enhancing URL input validation across various components.
    • Added new types and validation for provider authentication configurations, ensuring stricter adherence to expected URL formats.
    • Implemented a new end-to-end test for validating provider fields, ensuring proper error handling for invalid inputs.
  • Bug Fixes

    • Improved error handling and logging mechanisms across various provider configurations to enhance user feedback.
  • Documentation

    • Updated metadata descriptions for various fields to improve clarity and consistency in naming conventions.
  • Refactor

    • Streamlined component logic and prop management in several provider forms to enhance maintainability and readability.
  • Chores

    • Added new dependencies for enhanced validation capabilities.

Copy link

vercel bot commented Nov 11, 2024

@theedigerati is attempting to deploy a commit to the KeepHQ Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Nov 11, 2024

CLA assistant check
All committers have signed the CLA.

@theedigerati theedigerati marked this pull request as ready for review November 11, 2024 05:38
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. Enhancement New feature or request labels Nov 11, 2024
@dosubot dosubot bot added the Provider Providers related issues label Nov 11, 2024
@Matvey-Kuk Matvey-Kuk self-requested a review November 11, 2024 08:13
@theedigerati
Copy link
Author

Hello @Matvey-Kuk, I'd really appreciate your review and feedback on this contribution.
Thank you.

@Kiryous
Copy link
Contributor

Kiryous commented Nov 19, 2024

@coderabbitai review

Copy link

coderabbitai bot commented Nov 19, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Nov 19, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes in this pull request introduce a comprehensive validation framework across various provider configurations, primarily focusing on URL and port validation using Pydantic types. New types such as HttpsUrl, NoSchemeUrl, and UrlPort are implemented to enforce stricter validation rules. Additionally, several components have been refactored to streamline prop management and enhance error handling. A new end-to-end test has been added to validate provider inputs, ensuring that the application correctly handles various input scenarios.

Changes

File Path Change Summary
keep-ui/app/providers/form-validation.ts Introduced URL validation module with types and functions for URL validation and error handling.
keep-ui/app/providers/provider-form-scopes.tsx Removed installedProvidersMode and updated onRevalidate prop for clarity.
keep-ui/app/providers/provider-form.tsx Refactored ProviderForm for better state management and validation using new utility functions.
keep-ui/app/providers/providers-tiles.tsx Simplified state management by removing formValues and formErrors.
keep-ui/app/providers/providers.tsx Enhanced ProviderAuthConfig with new validation types and properties.
keep-ui/app/workflows/workflow-tile.tsx Removed form state management from WorkflowTile and WorkflowTileOld.
keep-ui/package.json Added dependency for zod library.
keep/parser/parser.py Adjusted method signatures and improved logging in provider parsing methods.
keep/providers/*.py Updated various provider configurations to utilize new validation types for URLs and ports.
keep/validation/fields.py Introduced custom URL classes extending Pydantic for enhanced validation.
tests/e2e_tests/test_end_to_end.py Added new test function for validating provider inputs and error messages.

Assessment against linked issues

Objective Addressed Explanation
Cross-all-providers accepted schemas for provider inputs (URLs, ports, etc.) (#1327)
Enforce validation on both frontend and backend (#1327)
Ensure correct handling of URLs with/without trailing slashes (#1327)
Validate various URL formats (http, https, localhost, etc.) (#1327)
Distinguish between host and URL in validation (#1327)

🐰 In the meadow, where the URLs play,
A rabbit hops, validating all day.
With types so strict, and checks so keen,
No invalid input shall be seen!
From ports to hosts, all will comply,
In our garden of code, we reach for the sky! 🌼


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:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • 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 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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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

🧹 Outside diff range and nitpick comments (96)
keep/providers/google_chat_provider/google_chat_provider.py (1)

Line range hint 50-76: Consider adding retry mechanism for notification failures

While the error handling is in place, network issues with webhook calls are common. A retry mechanism with exponential backoff would improve reliability.

Here's a suggested implementation:

+ from tenacity import retry, stop_after_attempt, wait_exponential
+
+ @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
  def _notify(self, message="", **kwargs: dict):
      """
      Notify a message to a Google Chat room using a webhook URL.

This would retry failed notifications up to 3 times with exponential backoff.

keep/providers/mattermost_provider/mattermost_provider.py (2)

61-62: Remove or properly document commented channel code

The comment about channel parameter being "bugged" lacks clarity. If the channel parameter is truly unnecessary due to webhook URL design, consider:

  1. Removing the commented code entirely
  2. Removing the unused channel parameter from the method signature
  3. Or if this is a known issue to be fixed, add a TODO with issue reference
-    # channel is currently bugged (and unnecessary, as a webhook url is already one per channel) and so it is ignored for now
-    # if channel:
-    #    payload["channel"] = channel
+    # Note: Channel parameter is not needed as Mattermost webhook URLs are channel-specific

Line range hint 44-70: Add type hints and improve security in _notify method

Several improvements could enhance type safety and security:

  1. verify=False disables SSL verification, which is unsafe for production
  2. Missing type hints on method parameters
  3. No validation for message/blocks parameters
-    def _notify(self, message="", blocks=[], channel="", **kwargs: dict):
+    def _notify(
+        self,
+        message: str = "",
+        blocks: list[dict] = [],
+        channel: str = "",
+        **kwargs: dict
+    ) -> None:
         """
         Notify alert message to Mattermost using the Mattermost Incoming Webhook API
         https://docs.mattermost.com/developer/webhooks-incoming.html
 
         Args:
+            message (str): The message to send
+            blocks (list[dict]): Message blocks for formatting
+            channel (str): Deprecated - Channel is determined by webhook URL
             kwargs (dict): The providers with context
         """
         self.logger.info("Notifying alert message to Mattermost")
         if not message:
             message = blocks[0].get("text")
         webhook_url = self.authentication_config.webhook_url
         payload = {"text": message, "blocks": blocks}
 
-        response = requests.post(webhook_url, json=payload, verify=False)
+        response = requests.post(str(webhook_url), json=payload)
keep-ui/app/providers/providers.tsx (2)

14-15: Consider adding type safety for select options.

While the new properties enhance configuration flexibility, consider adding type validation to ensure the default value matches the options array when type is "select".

Consider this type refinement:

export interface ProviderAuthConfig {
  // ... other properties
  type?: "select" | "form" | "file" | "switch";
  options?: Array<string | number>;
  default: string | number | boolean | null;
}

// Add a conditional type to ensure type safety
export type ValidatedProviderAuthConfig<T extends ProviderAuthConfig> = T extends { type: "select" }
  ? T & {
      options: NonEmptyArray<string | number>;
      default: T['options'][number];
    }
  : T;

18-18: Consider adding JSDoc comments for type options.

The type refinement improves type safety, but documentation about when to use each type would be helpful for maintainability.

Add JSDoc comments like this:

export interface ProviderAuthConfig {
  /** Type of form input to render:
   * - "select": Dropdown with predefined options
   * - "form": Standard text input
   * - "file": File upload input
   * - "switch": Toggle switch for boolean values
   */
  type?: "select" | "form" | "file" | "switch";
}
keep/providers/teams_provider/teams_provider.py (1)

Line range hint 65-77: Consider enhancing error handling and request reliability

While the type safety improvements are great, consider these additional enhancements:

  1. Add specific error handling for common HTTP status codes
  2. Implement request timeouts
  3. Add retry mechanism for transient failures

Here's a suggested implementation:

+ from requests.exceptions import RequestException
+ from tenacity import retry, stop_after_attempt, wait_exponential

+ @retry(
+     stop=stop_after_attempt(3),
+     wait=wait_exponential(multiplier=1, min=4, max=10)
+ )
  def _notify(self, message="", typeCard="MessageCard", themeColor=None, sections=[], **kwargs: dict):
      self.logger.debug("Notifying alert message to Teams")
      webhook_url = self.authentication_config.webhook_url
-     response = requests.post(
-         webhook_url,
-         json={
-             "@type": typeCard,
-             "themeColor": themeColor,
-             "text": message,
-             "sections": sections,
-         },
-     )
-     if not response.ok:
-         raise ProviderException(
-             f"{self.__class__.__name__} failed to notify alert message to Teams: {response.text}"
-         )
+     try:
+         response = requests.post(
+             webhook_url,
+             json={
+                 "@type": typeCard,
+                 "themeColor": themeColor,
+                 "text": message,
+                 "sections": sections,
+             },
+             timeout=10
+         )
+         response.raise_for_status()
+     except requests.exceptions.Timeout:
+         raise ProviderException("Timeout while sending message to Teams")
+     except requests.exceptions.HTTPError as e:
+         if e.response.status_code == 429:
+             raise ProviderException("Rate limit exceeded for Teams webhook")
+         elif e.response.status_code == 404:
+             raise ProviderException("Teams webhook URL not found")
+         else:
+             raise ProviderException(f"Failed to send message to Teams: {str(e)}")
+     except RequestException as e:
+         raise ProviderException(f"Network error while sending message to Teams: {str(e)}")
keep-ui/app/providers/providers-tiles.tsx (1)

118-120: Consider using design system breakpoints

The hardcoded breakpoint values (640px, 1024px) might not align with your design system's breakpoints. Consider using Tailwind's breakpoint constants for consistency.

Here's a suggested approach using Tailwind's screen configuration:

import resolveConfig from 'tailwindcss/resolveConfig'
import tailwindConfig from '../../../tailwind.config'

const { theme } = resolveConfig(tailwindConfig)
const screens = theme?.screens as { sm: string; lg: string }

const usePanelSize = () => {
  const [size, setSize] = useState(40);

  useEffect(() => {
    const updateSize = () => {
      const width = window.innerWidth;
      const sm = parseInt(screens.sm); // typically 640px
      const lg = parseInt(screens.lg); // typically 1024px
      setSize(width < sm ? 100 : width < lg ? 80 : 40);
    };

    updateSize();
    window.addEventListener('resize', updateSize);
    return () => window.removeEventListener('resize', updateSize);
  }, []);

  return size;
};
keep-ui/app/providers/provider-form-scopes.tsx (3)

37-42: Add aria-label to improve button accessibility

While the button implementation is correct, consider adding an aria-label for better accessibility.

 <Button
   color="gray"
   size="xs"
   icon={ArrowPathIcon}
   onClick={onRevalidate}
   variant="secondary"
   loading={refreshLoading}
+  aria-label="Refresh scopes validation status"
 >

61-67: Enhance validation logic robustness

Consider these improvements for better maintainability and type safety:

  1. Extract the magic number to a named constant
  2. Add null check for the validation message
  3. Consider using optional chaining instead of non-null assertion
+const MAX_VALIDATION_MESSAGE_LENGTH = 100;
+
 const ProviderFormScopes = ({ /* ... */ }) => {
   return (
     /* ... */
-    {provider.scopes!.map((scope) => {
+    {provider.scopes?.map((scope) => {
       let isScopeString =
-        typeof validatedScopes[scope.name] === "string";
+        validatedScopes[scope.name] != null && 
+        typeof validatedScopes[scope.name] === "string";
       let isScopeLong = false;

       if (isScopeString) {
         isScopeLong =
-          validatedScopes[scope.name].toString().length > 100;
+          validatedScopes[scope.name].toString().length > MAX_VALIDATION_MESSAGE_LENGTH;
       }

86-95: Simplify status display logic

Consider extracting the status logic into constants and mappings for better maintainability.

+const VALIDATION_STATUS = {
+  VALID: 'Valid',
+  NOT_CHECKED: 'Not checked',
+} as const;
+
+const getStatusColor = (status: any) => {
+  if (status === true) return 'emerald';
+  if (status === undefined) return 'gray';
+  return 'red';
+};
+
+const getStatusText = (status: any) => {
+  if (status === true) return VALIDATION_STATUS.VALID;
+  if (status === undefined) return VALIDATION_STATUS.NOT_CHECKED;
+  return status;
+};

 <Badge
-  color={
-    validatedScopes[scope.name] === true
-      ? "emerald"
-      : validatedScopes[scope.name] === undefined
-        ? "gray"
-        : "red"
-  }
+  color={getStatusColor(validatedScopes[scope.name])}
   className={`truncate ${isScopeLong ? "max-w-lg" : "max-w-xs"}`}
 >
-  {validatedScopes[scope.name] === true
-    ? "Valid"
-    : validatedScopes[scope.name] === undefined
-      ? "Not checked"
-      : validatedScopes[scope.name]}
+  {getStatusText(validatedScopes[scope.name])}
 </Badge>
keep/providers/discord_provider/discord_provider.py (1)

Line range hint 82-83: Consider enhancing error handling for URL-specific issues

The TODO comment indicates a need for more error handling. Consider adding specific handling for URL-related errors (e.g., SSL errors, connection timeouts) to provide better error messages.

Example enhancement:

         if response.status_code != 204:
             try:
                 r = response.json()
+            except requests.exceptions.SSLError as e:
+                raise ProviderException(
+                    f"{self.__class__.__name__} SSL verification failed for webhook URL: {str(e)}"
+                )
+            except requests.exceptions.ConnectionError as e:
+                raise ProviderException(
+                    f"{self.__class__.__name__} Failed to connect to webhook URL: {str(e)}"
+                )
             # unknown response
             except Exception:
                 raise ProviderException(
keep/providers/auth0_provider/auth0_provider.py (1)

82-82: Consider enhancing error handling for the API request.

While the URL construction is now safer with HttpsUrl, the API request could benefit from more specific error handling.

Consider adding specific error handling:

-        response = requests.get(url, headers=headers, params=params)
-        response.raise_for_status()
+        try:
+            response = requests.get(url, headers=headers, params=params)
+            response.raise_for_status()
+        except requests.exceptions.RequestException as e:
+            raise Exception(f"Failed to query Auth0 API: {str(e)}") from e
keep/providers/bigquery_provider/bigquery_provider.py (2)

Line range hint 16-31: Consider enhancing JSON validation in BigqueryProviderAuthConfig.

While the file type validation is good, consider adding content validation for the service account JSON to ensure it contains required fields (like private_key, client_email, etc.).

Example enhancement:

@pydantic.dataclasses.dataclass
class BigqueryProviderAuthConfig:
    """
    BigQuery authentication configuration.
    """
+   class ServiceAccountJSON(pydantic.BaseModel):
+       type: str
+       project_id: str
+       private_key_id: str
+       private_key: str
+       client_email: str
+       client_id: str
+       auth_uri: str = "https://accounts.google.com/o/oauth2/auth"
+       token_uri: str = "https://oauth2.googleapis.com/token"
+       auth_provider_x509_cert_url: str = "https://www.googleapis.com/oauth2/v1/certs"
+       client_x509_cert_url: str

    service_account_json: str = dataclasses.field(
        metadata={
            "required": True,
            "description": "The service account JSON with container.viewer role",
            "sensitive": True,
            "type": "file",
            "name": "service_account_json",
            "file_type": "application/json",
+           "validators": [ServiceAccountJSON],
        },
    )

Line range hint 42-56: Consider improving project_id validation.

The project_id validation logic in validate_config could be simplified by using Pydantic's built-in validators.

Example enhancement:

@pydantic.dataclasses.dataclass
class BigqueryProviderAuthConfig:
    project_id: Optional[str] = dataclasses.field(
        default=None,
        metadata={
            "required": False,
            "description": "Google Cloud project ID. If not provided, "
            "it will try to fetch it from the environment variable 'GOOGLE_CLOUD_PROJECT'",
        },
    )

+   @pydantic.validator("project_id", pre=True, always=True)
+   def validate_project_id(cls, v):
+       if v:
+           return v
+       project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
+       if not project_id:
+           raise ValueError("Project ID must be set in either the configuration or the 'GOOGLE_CLOUD_PROJECT' environment variable.")
+       return project_id
keep/providers/ssh_provider/ssh_provider.py (1)

Line range hint 98-103: Critical: RSA key assumption may break other key types

The __generate_client method uses RSAKey.from_private_key() which assumes RSA format, but the file type metadata accepts other key formats (Ed25519, etc.). This mismatch could cause runtime errors.

Consider updating the key loading logic to handle multiple formats:

-            key = RSAKey.from_private_key(
-                private_key_file, self.config.authentication.get("pkey_passphrase")
-            )
+            try:
+                # Try RSA first
+                key = RSAKey.from_private_key(
+                    private_key_file, self.config.authentication.get("pkey_passphrase")
+                )
+            except Exception as e:
+                # Reset file pointer
+                private_key_file.seek(0)
+                # Try other key types (Ed25519Key, ECDSAKey, etc.)
+                key = PKey.from_private_key(
+                    private_key_file, self.config.authentication.get("pkey_passphrase")
+                )
keep/providers/postgres_provider/postgres_provider.py (2)

Line range hint 117-141: Consider refactoring connection handling

The _query method has duplicate connection closing calls (in try block and finally). Consider refactoring to:

  1. Use a context manager for connection handling
  2. Consolidate connection management logic
-    def _query(self, query: str, **kwargs: dict) -> list | tuple:
+    def _query(self, query: str, **kwargs: dict) -> list:
         if not query:
             raise ValueError("Query is required")
 
         conn = self.__init_connection()
         try:
-            with conn.cursor() as cur:
-                # Open a cursor to perform database operations
-                cur = conn.cursor()
+            with conn.cursor() as cur:
                 # Execute a simple query
                 cur.execute(query)
                 # Fetch the results
                 results = cur.fetchall()
-                # Close the cursor and connection
-                cur.close()
-                conn.close()
-            return list(results)
+                return list(results)
         finally:
-            # Close the database connection
             conn.close()

Line range hint 144-166: Enhance error handling for database operations

The _notify method should include more specific error handling for common database operations failures.

     def _notify(self, query: str, **kwargs):
         if not query:
             raise ValueError("Query is required")
 
         conn = self.__init_connection()
         try:
             with conn.cursor() as cur:
-                # Open a cursor to perform database operations
-                cur = conn.cursor()
                 # Execute a simple query
                 cur.execute(query)
-                # Close the cursor and connection
-                cur.close()
                 conn.commit()
-                conn.close()
+        except psycopg2.Error as e:
+            self.logger.error(f"Database operation failed: {str(e)}")
+            raise
         finally:
-            # Close the database connection
             conn.close()
keep/providers/openshift_provider/openshift_provider.py (3)

37-42: Add security implications to insecure flag description

The description should include security implications of skipping TLS verification. This helps users make informed decisions.

-            "description": "Skip TLS verification",
+            "description": "Skip TLS verification (Warning: This poses security risks and should only be used in development environments)",

Line range hint 94-95: Consider making timeout configurable

The timeout value of 30 minutes (60 * 30) is hardcoded in multiple places. Consider making this configurable through the provider configuration.

+    timeout: int = dataclasses.field(
+        default=1800,  # 30 minutes
+        metadata={
+            "name": "timeout",
+            "description": "Timeout in seconds for OpenShift operations",
+            "required": False,
+            "sensitive": False,
+        },
+    )

Then use it in the methods:

-            with oc.timeout(60 * 30), oc.tracking() as t, client:
+            with oc.timeout(self.authentication_config.timeout), oc.tracking() as t, client:

Also applies to: 122-123


Line range hint 98-102: Enhance error messages with more context

The error messages could provide more specific information about what went wrong during login attempts. Consider including the API server URL (with sensitive parts redacted) and the specific OpenShift error message.

-                        self.logger.error("Error logging into the API server")
-                        raise Exception("Error logging into the API server")
+                        masked_url = str(self.authentication_config.api_server).replace(
+            self.authentication_config.api_server.host, "***")
+                        self.logger.error(f"Failed to log into the API server at {masked_url}")
+                        raise Exception(f"Authentication failed: {t.get_result().err}")
keep/providers/cilium_provider/cilium_provider.py (1)

Line range hint 16-28: Enhance type safety with custom Pydantic types

To align with the PR's objective of improving type safety and validation:

  1. Consider creating a custom Pydantic type for gRPC endpoints
  2. Move validation logic into the type itself rather than relying on metadata

Here's a suggested approach:

from pydantic import GetJsonSchema
from typing_extensions import Annotated

class GrpcEndpoint(str):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v: str) -> str:
        if not isinstance(v, str):
            raise TypeError('string required')
        if '://' in v:
            raise ValueError('protocol prefix not allowed in gRPC endpoint')
        if ':' not in v:
            raise ValueError('port number required in gRPC endpoint')
        return v

    @classmethod
    def __get_pydantic_json_schema__(cls, _schema_generator: GetJsonSchema) -> dict[str, Any]:
        return {
            "type": "string",
            "format": "grpc-endpoint",
            "example": "localhost:4245"
        }

@pydantic.dataclasses.dataclass
class CiliumProviderAuthConfig:
    """Cilium authentication configuration."""
    cilium_base_endpoint: Annotated[
        GrpcEndpoint,
        pydantic.Field(
            description="The base endpoint of the cilium hubble relay",
            examples=["localhost:4245"]
        )
    ]

This approach provides:

  • Strong type safety
  • Self-contained validation logic
  • Clear error messages
  • OpenAPI schema support
keep/providers/uptimekuma_provider/uptimekuma_provider.py (3)

Line range hint 89-106: Improve error handling and resource management in _get_heartbeats.

The current implementation has several areas for improvement:

  1. The API connection might not be properly closed if an error occurs
  2. The error handling is too broad
  3. The return type hint is missing

Consider refactoring like this:

-    def _get_heartbeats(self):
+    def _get_heartbeats(self) -> list[AlertDto]:
         api = None
         try:
             api = UptimeKumaApi(self.authentication_config.host_url)
             api.login(
                 self.authentication_config.username, self.authentication_config.password
             )
             response = api.get_heartbeats()
-            api.disconnect()
 
             length = len(response)
 
             if length == 0:
                 return []
 
             for key in response:
                 heartbeat = response[key][-1]
                 name = api.get_monitor(heartbeat["monitor_id"])["name"]
 
                 return AlertDto(
                     id=heartbeat["id"],
                     name=name,
                     monitor_id=heartbeat["monitor_id"],
                     description=heartbeat["msg"],
                     status=heartbeat["status"].name.lower(),
                     lastReceived=heartbeat["time"],
                     ping=heartbeat["ping"],
                     source=["uptimekuma"],
                 )
-        except Exception as e:
+        except ConnectionError as e:
             self.logger.error("Error getting heartbeats from UptimeKuma: %s", e)
-            raise Exception(f"Error getting heartbeats from UptimeKuma: {e}")
+            raise ConnectionError(f"Failed to connect to UptimeKuma: {e}")
+        except ValueError as e:
+            self.logger.error("Invalid data received from UptimeKuma: %s", e)
+            raise ValueError(f"Invalid UptimeKuma response: {e}")
+        finally:
+            if api:
+                api.disconnect()

Line range hint 108-115: Add type hints and improve error handling in _get_alerts.

The method should have proper type hints and more specific error handling.

Consider these improvements:

-    def _get_alerts(self) -> list[AlertDto]:
+    def _get_alerts(self) -> list[AlertDto]:
         try:
             self.logger.info("Collecting alerts (heartbeats) from UptimeKuma")
             alerts = self._get_heartbeats()
             return alerts
-        except Exception as e:
+        except (ConnectionError, ValueError) as e:
             self.logger.error("Error getting alerts from UptimeKuma: %s", e)
-            raise Exception(f"Error getting alerts from UptimeKuma: {e}")
+            raise

Line range hint 117-134: Add input validation to _format_alert method.

The static method lacks input validation for the event dictionary structure.

Consider using Pydantic models for the event structure:

class UptimeKumaMonitor(pydantic.BaseModel):
    id: str
    name: str
    url: str

class UptimeKumaHeartbeat(pydantic.BaseModel):
    status: str
    localDateTime: str
    msg: str

class UptimeKumaEvent(pydantic.BaseModel):
    monitor: UptimeKumaMonitor
    heartbeat: UptimeKumaHeartbeat
    msg: str

@staticmethod
def _format_alert(event: dict, provider_instance: "BaseProvider" = None) -> AlertDto:
    validated_event = UptimeKumaEvent.parse_obj(event)
    return AlertDto(
        id=validated_event.monitor.id,
        name=validated_event.monitor.name,
        monitor_url=validated_event.monitor.url,
        status=validated_event.heartbeat.status,
        description=validated_event.msg,
        lastReceived=validated_event.heartbeat.localDateTime,
        msg=validated_event.heartbeat.msg,
        source=["uptimekuma"],
    )
keep/providers/elastic_provider/elastic_provider.py (2)

30-37: Consider removing redundant validation metadata.

The validation: any_http_url metadata appears redundant since the field is already typed as pydantic.AnyHttpUrl. The type itself provides the validation.

     host: pydantic.AnyHttpUrl | None = dataclasses.field(
         default=None,
         metadata={
             "required": False,
             "description": "Elasticsearch host",
-            "validation": "any_http_url",
         },
     )

93-95: Consider adding error handling for validation failures.

While using Pydantic for validation is good, consider wrapping the config initialization in a try-except block to provide more user-friendly error messages.

     def validate_config(self):
         """
         Validate the provider config.
         """
-        self.authentication_config = ElasticProviderAuthConfig(
-            **self.config.authentication
-        )
+        try:
+            self.authentication_config = ElasticProviderAuthConfig(
+                **self.config.authentication
+            )
+        except pydantic.ValidationError as e:
+            raise ValueError(f"Invalid Elasticsearch provider configuration: {str(e)}")
keep/providers/grafana_oncall_provider/grafana_oncall_provider.py (1)

Line range hint 90-150: Consider using proper URL path joining.

While the type change to AnyHttpUrl is good, the URL construction using f-strings could be improved to handle trailing slashes correctly. Consider using urllib.parse.urljoin or similar for safer URL construction.

Example improvement:

- url=f"{self.authentication_config.host}/{self.API_URI}/OrgService.GetOrg",
+ from urllib.parse import urljoin
+ url=urljoin(str(self.authentication_config.host), f"{self.API_URI}/OrgService.GetOrg"),

This change would prevent potential issues with double slashes in URLs when the host has a trailing slash.

keep/providers/smtp_provider/smtp_provider.py (2)

30-38: Consider updating the required flag for smtp_port

The field has a sensible default value (587) but is marked as required. Consider setting "required": False since there's a default value, or document why it needs to be explicitly set despite having a default.

     smtp_port: UrlPort = dataclasses.field(
         metadata={
-            "required": True,
+            "required": False,
             "description": "SMTP port",
             "config_main_group": "authentication",
             "validation": "port",
         },
         default=587,
     )

21-38: Consider adding validation-specific test cases

While the main block provides basic testing, consider adding specific test cases for the new validation types:

  • Invalid server addresses (with schemes, invalid formats)
  • Edge case port numbers (0, negative, > 65535)
  • Different TLS/SSL configurations with various port combinations

This would help ensure the new validation logic handles all edge cases correctly and maintains backward compatibility.

keep/providers/redmine_provider/redmine_provider.py (1)

173-186: Improve error handling in _notify method

The error handling in the notify method could be enhanced:

  1. Use raise ... from e to preserve the exception chain
  2. Add more context to error messages
  3. Log the response content on error

Here's a suggested implementation:

         try:
             resp.raise_for_status()
         except HTTPError as e:
-            self.logger.error("Error While creating Redmine Issue")
+            error_details = {"status_code": resp.status_code}
+            try:
+                error_details["response"] = resp.json()
+            except ValueError:
+                error_details["response_text"] = resp.text
+            
+            self.logger.error(
+                "Failed to create Redmine issue",
+                extra=error_details
+            )
-            raise Exception(f"Failed to create issue: {str(e)}")
+            raise Exception(f"Failed to create Redmine issue: {str(e)}") from e
🧰 Tools
🪛 Ruff

182-182: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

keep/providers/gitlab_provider/gitlab_provider.py (2)

Line range hint 89-111: Security: Fix unsafe SSL handling and HTTP fallback

The current implementation has several security concerns:

  1. SSL verification is disabled (verify=False), which makes HTTPS connections vulnerable to MITM attacks
  2. Silent fallback to HTTP could expose sensitive data
  3. Security-relevant decisions aren't properly logged

Consider this safer implementation:

     @property
     def gitlab_host(self):
         if self._host:
             return self._host.rstrip("/")
 
         if self.authentication_config.host.startswith(
                 "http://"
         ) or self.authentication_config.host.startswith("https://"):
+            self.logger.warning("Using user-provided protocol: %s", 
+                urllib.parse.urlparse(self.authentication_config.host).scheme)
             self._host = self.authentication_config.host
             return self.authentication_config.host.rstrip("/")
 
         try:
             requests.get(
                 f"https://{self.authentication_config.host}",
-                verify=False,
+                verify=True,
             )
             self.logger.debug("Using https")
             self._host = f"https://{self.authentication_config.host}"
             return self._host.rstrip("/")
-        except requests.exceptions.SSLError:
-            self.logger.debug("Using http")
-            self._host = f"http://{self.authentication_config.host}"
+        except requests.exceptions.SSLError as e:
+            self.logger.error("SSL verification failed: %s", str(e))
+            raise ValueError(
+                "SSL verification failed. Ensure the GitLab host has a valid certificate"
+            )
         except Exception:
             return self.authentication_config.host.rstrip("/")

Line range hint 146-161: Improve error handling and remove debug prints

The _notify method has several areas for improvement:

  1. Debug print statements in production code
  2. Unvalidated kwargs passed directly to the API
  3. Generic error handling

Consider this improved implementation:

     def _notify(self, id: str, title: str, description: str = "", labels: str = "", issue_type: str = "issue",
                 **kwargs: dict):
         id = urllib.parse.quote(id, safe='')
-        print(id)
+        self.logger.debug("Creating issue for project ID: %s", id)
         params = self.__build_params_from_kwargs(
             kwargs={**kwargs, 'title': title, 'description': description, 'labels': labels, 'issue_type': issue_type})
-        print(self.gitlab_host)
+        
+        # Validate known parameters
+        allowed_params = {'title', 'description', 'labels', 'issue_type', 'assignee_id', 'milestone_id'}
+        unknown_params = set(kwargs.keys()) - allowed_params
+        if unknown_params:
+            self.logger.warning("Unknown parameters passed to notify: %s", unknown_params)
+
         resp = requests.post(f"{self.gitlab_host}/api/v4/projects/{id}/issues", headers=self.__get_auth_header(),
                              params=params)
         try:
             resp.raise_for_status()
         except HTTPError as e:
-            raise Exception(f"Failed to create issue: {str(e)}")
+            error_detail = e.response.json() if e.response.headers.get('content-type') == 'application/json' else str(e)
+            raise Exception(f"Failed to create GitLab issue: {error_detail}")
         return resp.json()
keep/providers/mongodb_provider/mongodb_provider.py (2)

20-27: Fix typo in host field hint message

The hint message contains a typo in "paassword".

-            "hint": "any valid mongo host_uri like mongodb://host:port, user:paassword@host:port?authSource",
+            "hint": "any valid mongo host_uri like mongodb://host:port, user:password@host:port?authSource",

The change to pydantic.AnyUrl type is a good improvement for validation.


125-127: Consider making timeout configurable

The hardcoded timeout value of 10 seconds might not be suitable for all environments. Consider making it configurable through the provider configuration.

+    DEFAULT_TIMEOUT_MS = 10000
+
     def __generate_client(self):
-        client = MongoClient(
-            **client_conf, **additional_options, serverSelectionTimeoutMS=10000
-        )  # 10 seconds timeout
+        timeout_ms = additional_options.get('serverSelectionTimeoutMS', self.DEFAULT_TIMEOUT_MS)
+        client = MongoClient(
+            **client_conf,
+            **additional_options,
+            serverSelectionTimeoutMS=timeout_ms
+        )
keep/providers/ntfy_provider/ntfy_provider.py (1)

33-41: Enhance field documentation with validation rules.

The metadata description could be more specific about the URL validation rules to help users provide correct input.

     host: pydantic.AnyHttpUrl | None = dataclasses.field(
         metadata={
             "required": False,
-            "description": "Ntfy Host URL (For self-hosted Ntfy only)",
+            "description": "Ntfy Host URL (For self-hosted Ntfy only). Must be a valid HTTP/HTTPS URL with protocol.",
             "sensitive": False,
             "hint": "http://localhost:80",
             "validation": "any_http_url",
         },
         default=None,
     )
keep/providers/webhook_provider/webhook_provider.py (2)

Line range hint 82-89: Enhance URL validation security measures.

The current blacklist-based validation might be insufficient to prevent Server-Side Request Forgery (SSRF) attacks. Consider:

  1. Using a whitelist approach for allowed domains
  2. Validating URL schemes (restrict to http/https)
  3. Implementing network-level restrictions

Example implementation:

ALLOWED_SCHEMES = {'http', 'https'}
ALLOWED_DOMAINS = {'api.example.com', 'webhook.internal'}

def __validate_url(self, url: str):
    parsed = urllib.parse.urlparse(url)
    if parsed.scheme not in self.ALLOWED_SCHEMES:
        raise ValueError(f"URL scheme must be one of {self.ALLOWED_SCHEMES}")
    if parsed.hostname not in self.ALLOWED_DOMAINS:
        raise ValueError(f"Domain not in allowed list: {parsed.hostname}")

Line range hint 156-187: Implement request timeouts and response size limits.

The HTTP request handling could be improved to handle edge cases and prevent potential issues:

  1. Missing request timeouts could lead to hanging connections
  2. Large responses could cause memory issues
  3. No retry mechanism for transient failures

Example improvements:

def _query(self, url: str, ..., timeout: int = 30, max_response_size: int = 10_485_760):
    # Add timeout and size limit
    if method == "GET":
        response = requests.get(
            url,
            headers=headers,
            params=params,
            timeout=timeout,
            stream=True,  # Stream to check size
            **kwargs
        )
        # Check response size
        content_length = response.headers.get('Content-Length')
        if content_length and int(content_length) > max_response_size:
            response.close()
            raise ValueError(f"Response too large: {content_length} bytes")
keep/providers/grafana_incident_provider/grafana_incident_provider.py (3)

24-32: Enhance validation metadata documentation

While the type safety improvement using HttpsUrl is excellent, consider enhancing the validation metadata to be more specific about URL requirements:

     host_url: HttpsUrl = dataclasses.field(
         metadata={
             "required": True,
-            "description": "Grafana Host URL",
+            "description": "Grafana Host URL (must be HTTPS)",
             "sensitive": False,
             "validation": "https_url",
+            "example": "https://grafana.example.com",
         },
         default=None,
     )

128-131: Consider making API endpoint paths more maintainable

The hardcoded API paths could be made more maintainable:

+    API_INCIDENTS_PATH = "/api/plugins/grafana-incident-app/resources/api/v1/IncidentsService.QueryIncidentPreviews"
+
     def _get_alerts(self) -> list[AlertDto]:
         try:
             response = requests.post(
                 urljoin(
                     self.authentication_config.host_url,
-                    "/api/plugins/grafana-incident-app/resources/api/v1/IncidentsService.QueryIncidentPreviews",
+                    self.API_INCIDENTS_PATH,
                 ),

This change would:

  1. Make the API paths more maintainable
  2. Reduce duplication (same path is used in validate_scopes)
  3. Make it easier to update API versions in the future

198-201: Consider more robust environment variable handling

While the error message is clear, consider using a more robust pattern for environment variable validation:

-    if host_url is None or api_token is None:
-        raise Exception(
-            "GRAFANA_HOST_URL and GRAFANA_SERVICE_ACCOUNT_TOKEN environment variables are required"
-        )
+    required_env_vars = {
+        "GRAFANA_HOST_URL": host_url,
+        "GRAFANA_SERVICE_ACCOUNT_TOKEN": api_token,
+    }
+    missing_vars = [name for name, value in required_env_vars.items() if not value]
+    if missing_vars:
+        raise Exception(
+            f"Missing required environment variables: {', '.join(missing_vars)}"
+        )

This approach:

  1. Makes it easier to add new required variables
  2. Provides more specific error messages
  3. Follows a more maintainable pattern
keep/providers/kubernetes_provider/kubernetes_provider.py (1)

Line range hint 82-91: Consider adding test cases for URL validation edge cases.

The __create_k8s_client method uses the validated URL, but we should ensure proper test coverage for various URL formats and error cases:

  • Invalid URLs
  • URLs with different protocols
  • URLs with and without ports
  • Internal cluster URLs

Consider adding test cases to verify the URL validation behavior:

def test_kubernetes_provider_url_validation():
    # Test valid URLs
    valid_configs = [
        {"api_server": "https://api.k8s.example.com", "token": "test"},
        {"api_server": "https://localhost:6443", "token": "test"},
        {"api_server": "https://10.0.0.1:6443", "token": "test"},
    ]
    
    # Test invalid URLs
    invalid_configs = [
        {"api_server": "not-a-url", "token": "test"},
        {"api_server": "http://api.k8s.example.com", "token": "test"},  # If only https is allowed
    ]
keep/providers/squadcast_provider/squadcast_provider.py (2)

Line range hint 144-144: Fix typo in method name '_crete_notes'

The method name contains a typo: '_crete_notes' should be '_create_notes'.

-    def _crete_notes(
+    def _create_notes(

Line range hint 140-144: Add type hints to method parameters

Consider adding type hints to improve code maintainability and type safety. This aligns with the PR's focus on type safety.

     def _create_incidents(
         self,
-        headers: dict,
-        message: str,
-        description: str,
-        tags: dict = {},
-        priority: str = "",
-        status: str = "",
-        event_id: str = "",
-        additional_json: str = "",
+        headers: dict[str, str],
+        message: str,
+        description: str,
+        tags: dict[str, str] = {},
+        priority: str = "",
+        status: str = "",
+        event_id: str = "",
+        additional_json: str = "",
     ) -> requests.Response:
keep/providers/gke_provider/gke_provider.py (3)

28-28: LGTM! Consider enhancing service account JSON validation.

The change to use MIME type application/json instead of .json extension is good. However, consider strengthening the validation further:

 service_account_json: str = dataclasses.field(
     metadata={
         "required": True,
         "description": "The service account JSON with container.viewer role",
         "sensitive": True,
         "type": "file",
         "name": "service_account_json",
         "file_type": "application/json",
+        "max_size_bytes": 16384,  # 16KB should be more than enough for service account JSON
+        "json_schema": {
+            "type": "object",
+            "required": ["type", "project_id", "private_key_id", "private_key", "client_email"],
+            "properties": {
+                "type": {"const": "service_account"}
+            }
+        }
     }
 )

Line range hint 1-199: Enhance type safety across the implementation.

While the PR adds basic Pydantic validation, there are several opportunities to improve type safety:

  1. Use specific exceptions instead of catching generic Exception
  2. Convert command types to an enum
  3. Add comprehensive type hints
  4. Document and type the query kwargs

Here's a suggested approach:

from enum import Enum
from typing import Dict, List, Optional, TypedDict, Union

class GkeCommandType(Enum):
    GET_PODS = "get_pods"
    GET_PVC = "get_pvc"
    GET_NODE_PRESSURE = "get_node_pressure"

class NodePressure(TypedDict):
    name: str
    conditions: List[Dict]

class GkeProvider(BaseProvider):
    def __init__(
        self,
        context_manager: ContextManager,
        provider_id: str,
        config: ProviderConfig
    ) -> None:
        try:
            self._service_account_data = json.loads(
                self.authentication_config.service_account_json
            )
        except json.JSONDecodeError as e:
            self._service_account_data = None
            raise ValueError(f"Invalid service account JSON: {e}")
        
    def _query(
        self,
        command_type: GkeCommandType,
        **kwargs: Dict[str, Union[str, int, bool]]
    ) -> Union[List[Dict], List[NodePressure]]:
        """Query GKE resources.
        
        Args:
            command_type: The type of query to perform
            **kwargs: Additional arguments for the query
                     Supported kwargs depend on command_type:
                     - GET_PODS: namespace (str, optional)
                     - GET_PVC: namespace (str, optional)
                     - GET_NODE_PRESSURE: No additional kwargs
        
        Returns:
            List of resources matching the query
        
        Raises:
            NotImplementedError: If command_type is not supported
        """

Line range hint 89-91: Strengthen input validation for region and cluster_name.

The current validation is minimal. Consider adding proper validation rules for GCP regions and GKE cluster names.

@pydantic.dataclasses.dataclass
class GkeProviderAuthConfig:
    """GKE authentication configuration."""
    
    service_account_json: str = dataclasses.field(...)
    
-    cluster_name: str = dataclasses.field(
+    cluster_name: pydantic.constr(
+        regex=r'^[a-z0-9-]{1,40}$'
+    ) = dataclasses.field(
         metadata={"required": True, "description": "The name of the cluster"}
     )
-    region: str = dataclasses.field(
+    region: pydantic.constr(
+        regex=r'^[a-z]+-[a-z]+\d+(?:-[a-z])?$'
+    ) = dataclasses.field(
         default="us-central1",
         metadata={
             "required": False,
             "description": "The GKE cluster region",
             "hint": "us-central1",
         },
     )
keep/providers/opsgenie_provider/opsgenie_provider.py (3)

Line range hint 24-28: Add type validation for OpsGenieRecipient

The type field should be validated against OpsGenie's specific recipient types for better type safety.

Consider using Pydantic's literal types:

+from typing import Literal
+
 class OpsGenieRecipient(pydantic.BaseModel):
     # https://github.com/opsgenie/opsgenie-python-sdk/blob/master/docs/Recipient.md
-    type: str
+    type: Literal["team", "user", "escalation", "schedule"]
     id: typing.Optional[str] = None

Line range hint 89-106: Add comprehensive input validation for alert creation parameters

Several parameters in _create_alert could benefit from stronger type validation to prevent invalid inputs early.

Consider creating a validated model for alert creation:

from typing import Literal
from pydantic import BaseModel, HttpUrl, constr

class CreateAlertRequest(BaseModel):
    user: Optional[constr(max_length=100)]
    note: Optional[constr(max_length=25000)]
    source: Optional[HttpUrl]
    message: constr(min_length=1, max_length=130)
    alias: Optional[constr(max_length=512)]
    description: Optional[constr(max_length=25000)]
    responders: Optional[List[OpsGenieRecipient]]
    visible_to: Optional[List[OpsGenieRecipient]]
    actions: Optional[List[str]]
    tags: Optional[List[str]]
    details: Optional[Dict[str, str]]
    entity: Optional[str]
    priority: Optional[Literal["P1", "P2", "P3", "P4", "P5"]]

Then modify the method signature:

-    def _create_alert(
-        self,
-        user: str | None = None,
-        note: str | None = None,
-        ...
+    def _create_alert(self, request: CreateAlertRequest):

Line range hint 196-208: Add validation for query parameters

The _query method should validate the query_type parameter against allowed values.

Consider using a validated model:

class QueryRequest(BaseModel):
    query_type: Literal["alerts"]
    query: str

def _query(self, request: QueryRequest, **kwargs: dict):
    api_instance = opsgenie_sdk.AlertApi(opsgenie_sdk.ApiClient(self.configuration))
    if request.query_type == "alerts":
        alerts = api_instance.list_alerts(query=request.query)
    else:
        raise NotImplementedError(f"Query type {request.query_type} not implemented")
keep/providers/victoriametrics_provider/victoriametrics_provider.py (2)

Line range hint 109-125: Ensure comprehensive testing of HTTP interactions.

The URL validation changes affect multiple HTTP interaction points:

  • validate_scopes(): Client connection validation
  • _get_alerts(): Alert retrieval
  • _query(): Query execution

Consider adding integration tests to verify these interactions with the new URL validation.

Would you like me to help create integration tests covering these HTTP interaction scenarios?

Also applies to: 186-241


The URL handling logic needs to be updated for AnyHttpUrl type safety

The current implementation has two issues with the new AnyHttpUrl type:

  1. The manual protocol detection logic is redundant since AnyHttpUrl already validates and requires a protocol (http/https) in the URL
  2. The URL construction with string concatenation (f"https://{host}") bypasses the type safety provided by AnyHttpUrl

Required changes:

  • Remove the manual protocol detection logic since VMAlertURL and VMAlertHost are already validated as proper URLs by AnyHttpUrl
  • Use the validated URL directly from authentication_config.VMAlertURL or VMAlertHost without string manipulation
  • Remove the SSL error handling as it's no longer needed with validated URLs

The code should trust the URL validation done by the Pydantic type system rather than implementing its own protocol detection.

🔗 Analysis chain

Line range hint 142-185: Verify URL handling logic with new types.

The vmalert_host property performs URL construction and protocol detection. With the new AnyHttpUrl type, verify that:

  1. The URL construction logic handles the new types correctly
  2. The protocol detection still works as expected with the validated URLs

Let me gather more information about the URL validation types to make a complete assessment.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check URL handling patterns across the codebase

# Check for similar URL construction patterns that might need updates
rg -A 5 "startswith\(\"https://\"\) or .startswith\(\"http://\"\)"

# Look for other protocol detection implementations
rg -A 5 "requests.exceptions.SSLError"

Length of output: 3393


Script:

#!/bin/bash
# Check for AnyHttpUrl type usage and related URL validation
rg -A 5 "AnyHttpUrl"

# Check the authentication config class
ast-grep --pattern 'class VictoriametricsProviderAuthConfig'

Length of output: 13665

keep/providers/prometheus_provider/prometheus_provider.py (3)

Line range hint 21-28: Consider using URL path joining instead of string interpolation.

The URL validation is good, but the usage of string interpolation in _query() and _get_alerts() methods could potentially create malformed URLs. Consider using urllib.parse.urljoin() for safer URL path joining.

Example implementation:

+from urllib.parse import urljoin

 def _query(self, query):
-    response = requests.get(
-        f"{self.authentication_config.url}/api/v1/query",
+    response = requests.get(
+        urljoin(str(self.authentication_config.url), "api/v1/query"),
         params={"query": query},
         auth=auth,
     )

 def _get_alerts(self) -> list[AlertDto]:
-    response = requests.get(
-        f"{self.authentication_config.url}/api/v1/alerts",
+    response = requests.get(
+        urljoin(str(self.authentication_config.url), "api/v1/alerts"),
         auth=auth,
     )

Line range hint 21-28: Enhance error handling for URL-related issues.

With the stricter URL validation, consider improving error handling to provide more specific feedback for URL-related issues:

Example implementation:

from urllib.parse import urljoin
from requests.exceptions import RequestException

class PrometheusError(Exception):
    """Base exception for Prometheus provider."""
    pass

class PrometheusConnectionError(PrometheusError):
    """Raised when there are connection issues."""
    pass

def _query(self, query):
    try:
        response = requests.get(
            urljoin(str(self.authentication_config.url), "api/v1/query"),
            params={"query": query},
            auth=auth,
            timeout=30,  # Add timeout
        )
        response.raise_for_status()
    except RequestException as e:
        raise PrometheusConnectionError(f"Failed to query Prometheus: {str(e)}")
    return response.json()

URL field type change needs additional validation for API endpoints

The verification reveals that the URL is used with API path suffixes (/api/v1/query and /api/v1/alerts) through string concatenation. While pydantic.AnyHttpUrl validates the base URL format, there are potential issues:

  • The URL field's hint suggests a specific endpoint format (/api/prom), but the code uses different endpoints (/api/v1/...)
  • String concatenation with API paths could lead to double slashes if the base URL ends with a trailing slash

Suggested improvements:

  • Add a custom validator to ensure the base URL doesn't end with a trailing slash
  • Update the hint to match the actual API version used in the code (/api/v1/)
  • Consider using urljoin for safer URL path joining
🔗 Analysis chain

Line range hint 21-28: LGTM! The URL field type change enhances input validation.

The change from str to pydantic.AnyHttpUrl aligns well with the PR objectives to improve provider input validation. This ensures that only valid HTTP/HTTPS URLs are accepted.

Let's verify the URL usage in the codebase:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the URL is properly validated in all contexts
# Expected: No direct string concatenation with the URL that could bypass validation

# Check for potential URL string concatenation
rg -A 2 "f\"{.*?url.*?}.*?/" keep/providers/prometheus_provider/prometheus_provider.py

# Check for URL usage in query methods
ast-grep --pattern 'def _query($params) {
  $$$
  requests.get($url, $$$)
  $$$
}'

Length of output: 275


Script:

#!/bin/bash
# Let's try different approaches to verify URL usage

# Check how the URL is used in the prometheus provider
rg -A 3 "\.url" keep/providers/prometheus_provider/prometheus_provider.py

# Look for request calls that might use the URL
rg -A 3 "requests\.(get|post)" keep/providers/prometheus_provider/prometheus_provider.py

# Check the complete implementation of methods that might use URL
ast-grep --pattern 'def _query'
ast-grep --pattern 'def _get_alerts'

Length of output: 796

tests/e2e_tests/test_end_to_end.py (2)

156-159: Enhance test documentation

Consider expanding the docstring to describe the specific validation types being tested and the providers covered. This would make it easier for other developers to understand the test coverage.

Example enhancement:

def test_provider_validation(browser):
    """
-   Test field validation for provider fields.
+   Test field validation for provider configuration inputs.
+   
+   Validates:
+   - Required fields
+   - URL formats (http, https, no scheme)
+   - Port number ranges
+   - Top-level domain formats
+   
+   Providers tested:
+   - Kibana (any_http_url, port)
+   - Teams (https_url)
+   - Site24x7 (tld)
+   - MongoDB (any_url)
+   - PostgreSQL (no_scheme_url)
    """

164-267: Add additional URL validation test cases

While the current test coverage is good, consider adding these important validation scenarios:

  1. URLs with trailing slashes
  2. URLs with query parameters
  3. URLs containing special characters
  4. URLs with basic auth components

Also, consider reducing code duplication by extracting common validation patterns into helper functions:

def validate_url_field(browser, placeholder, test_cases):
    """Helper to test URL field validation with multiple test cases."""
    for url, should_pass in test_cases:
        browser.get_by_placeholder(placeholder).fill(url)
        browser.get_by_role("button", name="Connect", exact=True).click()
        if should_pass:
            expect(browser.locator("p.tremor-TextInput-errorMessage")).to_be_hidden()
        else:
            expect(browser.locator("p.tremor-TextInput-errorMessage")).to_have_count(1)

# Usage example:
test_cases = [
    ("https://example.com", True),
    ("https://example.com/", True),
    ("https://example.com?param=value", True),
    ("invalid-url", False)
]
validate_url_field(browser, "Enter webhook_url", test_cases)
keep/providers/appdynamics_provider/appdynamics_provider.py (1)

Line range hint 102-111: Consider removing redundant URL protocol validation.

Now that pydantic.AnyHttpUrl handles URL validation, the manual protocol check in validate_config might be redundant. Pydantic will ensure the URL has a valid scheme.

Consider this simplified version:

 def validate_config(self):
     """
     Validates required configuration for AppDynamics provider.
     """
     self.authentication_config = AppdynamicsProviderAuthConfig(
         **self.config.authentication
     )
-    if not self.authentication_config.host.startswith(
-        "https://"
-    ) and not self.authentication_config.host.startswith("http://"):
-        self.authentication_config.host = (
-            f"https://{self.authentication_config.host}"
-        )
keep/providers/splunk_provider/splunk_provider.py (1)

Line range hint 143-186: Enhance error handling for SSL-specific errors

The error handling should be extended to handle SSL-specific exceptions that might occur when verify=True.

Add specific handling for SSL errors:

         except ConnectionRefusedError:
             self.logger.exception(
                 "Error connecting to Splunk",
             )
             validated_scopes = dict(
                 [[scope.name, "CONNECTION_REFUSED"] for scope in self.PROVIDER_SCOPES]
             )
+        except ssl.SSLError as e:
+            self.logger.exception(
+                "SSL error connecting to Splunk",
+                extra={"error": str(e)}
+            )
+            validated_scopes = dict(
+                [[scope.name, "SSL_ERROR"] for scope in self.PROVIDER_SCOPES]
+            )
         except ParseError as e:
keep/providers/ilert_provider/ilert_provider.py (2)

47-54: LGTM: Enhanced type safety for ilert_host field

The change from str to HttpsUrl with validation metadata improves type safety and input validation. The default value is appropriate for the service.

Consider adding a description of accepted URL formats in the hint metadata to help users understand the validation requirements.

     ilert_host: HttpsUrl = dataclasses.field(
         metadata={
             "required": False,
             "description": "ILert API host",
-            "hint": "https://api.ilert.com/api",
+            "hint": "HTTPS URL (e.g., https://api.ilert.com/api)",
             "validation": "https_url"
         },
         default="https://api.ilert.com/api",
     )

108-114: Improve error handling for read_permission validation

Consider adding error details to the log message and implementing retry logic for transient failures.

     try:
         if scope.name == "read_permission":
             res = requests.get(
                 f"{self.authentication_config.ilert_host}/incidents",
                 headers={
                     "Authorization": self.authentication_config.ilert_token
                 },
+                timeout=30,  # Add timeout
             )
             res.raise_for_status()
+            self.logger.debug("Read permission validated successfully")
             scopes[scope.name] = True
keep/providers/servicenow_provider/servicenow_provider.py (2)

Line range hint 173-173: Security concern: SSL verification is disabled

While the code enforces HTTPS URLs through type validation, the requests are made with verify=False. This bypasses SSL certificate verification, potentially exposing the application to man-in-the-middle attacks.

Apply this change to enable SSL verification:

- verify=False,
+ verify=True,

Consider adding a configuration option if SSL verification needs to be optional for development/testing environments:

verify_ssl: bool = dataclasses.field(
    default=True,
    metadata={
        "required": False,
        "description": "Enable/disable SSL certificate verification",
        "sensitive": False,
    }
)

Also applies to: 271-271, 363-363


Line range hint 173-182: Refactor suggestion: Centralize HTTP request configuration

The code duplicates HTTP request configuration across multiple methods. Consider extracting this into a helper method to improve maintainability and consistency.

Consider adding a helper method:

def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
    """Make an HTTP request with standard configuration."""
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json"
    }
    if self._access_token:
        headers["Authorization"] = f"Bearer {self._access_token}"
    
    auth = None if self._access_token else (
        self.authentication_config.username,
        self.authentication_config.password,
    )
    
    return requests.request(
        method=method,
        url=url,
        headers=headers,
        auth=auth,
        verify=True,  # Enable SSL verification
        timeout=10,
        **kwargs
    )

Usage example:

response = self._make_request(
    "GET",
    f"{self.authentication_config.service_now_base_url}/api/now/table/{table_name}",
    params=kwargs
)

Also applies to: 271-280, 363-372

keep/providers/jira_provider/jira_provider.py (2)

Line range hint 166-172: Simplify jira_host property

With HttpsUrl type ensuring proper URL format, the protocol check becomes redundant.

     @property
     def jira_host(self):
-        host = (
-            self.authentication_config.host
-            if self.authentication_config.host.startswith("https://")
-            else f"https://{self.authentication_config.host}"
-        )
-        return host
+        return str(self.authentication_config.host)

SSL verification is disabled across multiple providers

The security concern about disabled SSL verification extends beyond the Jira provider. This is a widespread pattern found across multiple provider implementations:

  • VictoriaMetrics provider
  • Splunk provider
  • ServiceNow provider (multiple instances)
  • Mattermost provider
  • Redmine provider
  • GitLab provider
  • JiraOnPrem provider
  • Jira provider
  • Graylog provider
  • Grafana provider

Recommended solution:

  1. Add SSL verification configuration to the base provider configuration class
  2. Propagate this setting to all providers
  3. Default to verify=True for security
  4. Allow users to disable verification only when absolutely necessary (e.g., self-signed certificates in internal environments)

Example configuration addition:

class BaseProviderConfig:
    verify_ssl: bool = dataclasses.field(
        default=True,
        metadata={
            "required": False,
            "description": "Verify SSL certificates for HTTPS connections",
            "sensitive": False
        }
    )
🔗 Analysis chain

Line range hint 493-503: Security: SSL verification is disabled

While the URL construction is correct, the verify=False parameter in the requests call disables SSL certificate verification, which could expose the application to MITM attacks.

Let's check if this is a pattern across the codebase:

Consider:

  1. Enabling SSL verification by removing verify=False
  2. If SSL verification is problematic in certain environments, make it configurable:
@pydantic.dataclasses.dataclass
class JiraProviderAuthConfig:
    # ... existing fields ...
+    verify_ssl: bool = dataclasses.field(
+        default=True,
+        metadata={
+            "required": False,
+            "description": "Verify SSL certificates",
+            "sensitive": False
+        }
+    )
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other instances of disabled SSL verification
rg --type python "verify\s*=\s*False" -B 2 -A 2

Length of output: 80


Script:

#!/bin/bash
# Check for other instances of disabled SSL verification with ripgrep
# Using a simpler pattern and file extension
rg "verify=False" -g "*.py" -B 2 -A 2

Length of output: 20552

keep/providers/kibana_provider/kibana_provider.py (2)

35-42: Consider making the hint more generic

The hint example uses a specific domain which might not be applicable to all users. Consider using a more generic example.

-            "hint": "https://keep.kb.us-central1.gcp.cloud.es.io",
+            "hint": "https://your-kibana-instance.example.com",

43-49: Enhance port documentation

Consider adding valid port range information to the hint to help users understand the acceptable values.

             "required": False,
-            "description": "Kibana Port (defaults to 9243)",
+            "description": "Kibana Port (defaults to 9243, valid range: 1-65535)",
keep/providers/openobserve_provider/openobserve_provider.py (2)

49-54: Remove redundant validation metadata for openObserveHost.

The validation field in metadata is redundant since Pydantic's AnyHttpUrl type already enforces URL validation.

     openObserveHost: pydantic.AnyHttpUrl = dataclasses.field(
         metadata={
             "required": True,
             "description": "OpenObserve host url",
-            "hint": "e.g. http://localhost",
-            "validation": "any_http_url"
+            "hint": "e.g. http://localhost"
         },
     )

58-63: Remove redundant validation metadata for openObservePort.

The validation field in metadata is redundant since the custom UrlPort type already enforces port validation.

     openObservePort: UrlPort = dataclasses.field(
         metadata={
             "required": True,
             "description": "OpenObserve Port",
-            "hint": "e.g. 5080",
-            "validation": "port"
+            "hint": "e.g. 5080"
         },
     )
keep/providers/sentry_provider/sentry_provider.py (1)

37-45: Consider adding support for custom Sentry instances

While enforcing HTTPS is good security practice, some organizations might use custom Sentry instances with different URL patterns. Consider:

  1. Adding validation for custom domain patterns
  2. Documenting supported URL formats in the field's hint
     api_url: HttpsUrl = dataclasses.field(
         metadata={
             "required": False,
             "description": "Sentry API URL",
-            "hint": "https://sentry.io/api/0 (see https://docs.sentry.io/api/)",
+            "hint": "HTTPS URL to your Sentry instance API (e.g., https://sentry.io/api/0 or https://sentry.your-domain.com/api/0)",
             "sensitive": False,
             "validation": "https_url"
         },
         default="https://sentry.io/api/0",
     )
keep/providers/zabbix_provider/zabbix_provider.py (1)

Line range hint 326-327: Add trailing slash handling in URL construction.

When constructing the API endpoint URL, ensure consistent handling of trailing slashes to prevent potential issues. Consider using urllib.parse.urljoin for safer URL concatenation.

-        url = f"{self.authentication_config.zabbix_frontend_url}/api_jsonrpc.php"
+        from urllib.parse import urljoin
+        url = urljoin(str(self.authentication_config.zabbix_frontend_url), "api_jsonrpc.php")
keep/providers/grafana_provider/grafana_provider.py (6)

38-44: Consider using HttpUrl instead of AnyHttpUrl for stricter HTTPS enforcement

While using pydantic.AnyHttpUrl improves type safety, consider using pydantic.HttpUrl instead to enforce HTTPS, which is more secure for production environments. If HTTP support is required for local development, this should be explicitly documented.

-    host: pydantic.AnyHttpUrl = dataclasses.field(
+    host: pydantic.HttpUrl = dataclasses.field(
         metadata={
             "required": True,
             "description": "Grafana host",
-            "hint": "e.g. https://keephq.grafana.net",
+            "hint": "HTTPS URL (e.g. https://keephq.grafana.net). For local development, use HTTP URLs like http://localhost:3000",
             "validation": "any_http_url"
         },
     )

Line range hint 200-300: Security: SSL verification is disabled

The code uses verify=False in multiple HTTP requests, which disables SSL certificate verification. This is unsafe and could lead to man-in-the-middle attacks.

Consider:

  1. Enabling SSL verification by default
  2. Adding a configuration option for SSL verification
  3. Adding proper error handling for SSL certificate issues
- response = requests.get(contacts_api, verify=False, headers=headers)
+ response = requests.get(contacts_api, verify=True, headers=headers)

Line range hint 249-251: Inconsistent string formatting

The code mixes f-strings and .format(). For consistency and readability, stick to f-strings which are more readable and maintainable.

- self.logger.info('Creating webhook with name "{webhook_name}"')
+ self.logger.info(f'Creating webhook with name "{webhook_name}"')

Line range hint 234-236: Extract version comparison to a constant

The version comparison 9.4.7 is hardcoded in multiple places. Consider extracting it to a class constant for better maintainability.

+ MINIMUM_SUPPORTED_VERSION = "9.4.7"
+
  def setup_webhook(self, tenant_id: str, keep_api_url: str, api_key: str, setup_alerts: bool = True):
      # ...
-     if Version(grafana_version) > Version("9.4.7"):
+     if Version(grafana_version) > Version(self.MINIMUM_SUPPORTED_VERSION):

Line range hint 1-450: Add type hints to improve type safety

Since this PR focuses on type safety, consider adding type hints to method parameters and return values throughout the file. This would complement the Pydantic validation and make the code more maintainable.

Example for the setup_webhook method:

- def setup_webhook(self, tenant_id: str, keep_api_url: str, api_key: str, setup_alerts: bool = True):
+ def setup_webhook(
+     self,
+     tenant_id: str,
+     keep_api_url: str,
+     api_key: str,
+     setup_alerts: bool = True
+ ) -> None:

Line range hint 200-300: Refactor setup_webhook method for better maintainability

The setup_webhook method is quite long (100+ lines) and handles multiple responsibilities. Consider splitting it into smaller, focused methods:

  1. _get_contact_points
  2. _create_or_update_webhook
  3. _setup_alert_policies

This would improve readability and make the code easier to test.

keep/providers/graylog_provider/graylog_provider.py (2)

48-54: Consider removing redundant validation metadata.

The validation metadata entry "any_http_url" is redundant since the field type pydantic.AnyHttpUrl already enforces URL validation.

     deployment_url: pydantic.AnyHttpUrl = dataclasses.field(
         metadata={
             "required": True,
             "description": "Deployment Url",
             "hint": "Example: http://127.0.0.1:9000",
-            "validation": "any_http_url"
         },
     )

48-54: Refactor URL handling logic to leverage AnyHttpUrl validation.

Since deployment_url is now validated using pydantic.AnyHttpUrl, the complex URL handling in the graylog_host property could be simplified. The current implementation attempts HTTP/HTTPS connections which may not be necessary as the URL scheme is now guaranteed by the type.

Consider simplifying the graylog_host property:

     @property
     def graylog_host(self):
         self.logger.debug("Fetching Graylog host")
         if self._host:
             self.logger.debug("Returning cached Graylog host")
             return self._host
-
-        # Handle host determination logic with logging
-        if self.authentication_config.deployment_url.startswith(
-            "http://"
-        ) or self.authentication_config.deployment_url.startswith("https://"):
-            self.logger.info("Using supplied Graylog host with protocol")
-            self._host = self.authentication_config.deployment_url
-            return self._host
-
-        # Otherwise, attempt to use https
-        try:
-            self.logger.debug(
-                f"Trying HTTPS for {self.authentication_config.deployment_url}"
-            )
-            requests.get(
-                f"https://{self.authentication_config.deployment_url}",
-                verify=False,
-            )
-            self.logger.info("HTTPS protocol confirmed")
-            self._host = f"https://{self.authentication_config.deployment_url}"
-        except requests.exceptions.SSLError:
-            self.logger.warning("SSL error encountered, falling back to HTTP")
-            self._host = f"http://{self.authentication_config.deployment_url}"
-        except Exception as e:
-            self.logger.error(
-                "Failed to determine Graylog host", extra={"exception": str(e)}
-            )
-            self._host = self.authentication_config.deployment_url.rstrip("/")
-
+        self._host = str(self.authentication_config.deployment_url).rstrip("/")
         return self._host

Also applies to: 134-166

keep/parser/parser.py (4)

Line range hint 673-686: Enhance error handling for provider configuration

The current error handling for missing provider configurations could be improved:

  1. The fallback to empty authentication without validation could lead to runtime issues
  2. The warning message could be more specific about the required configuration format

Consider this improvement:

     if not provider_config:
         self.logger.warning(
-            "Provider not found in configuration, did you configure it?",
+            "Provider configuration missing or invalid. Please ensure the provider is properly configured in your providers.yaml or environment variables.",
             extra={
                 "provider_id": config_id,
                 "provider_type": provider_type,
                 "provider_config": provider_config,
                 "tenant_id": context_manager.tenant_id,
             },
         )
-        provider_config = {"authentication": {}}
+        raise ValueError(f"Missing required configuration for provider '{config_id}' of type '{provider_type}'")

317-319: LGTM: Good logging improvements

The addition of debug logging enhances observability. Consider standardizing the log message format across all debug statements for consistency.

Consider this format for consistency:

-    self.logger.debug("Workflow providers parsed successfully")
+    self.logger.debug("Successfully parsed workflow providers", extra={"workflow_id": workflow.get("id")})

Line range hint 1-824: Consider splitting Parser class into smaller, focused classes

The Parser class has grown to handle multiple responsibilities (workflows, providers, actions), violating the Single Responsibility Principle. This makes the code harder to maintain and test.

Consider:

  1. Split into separate classes:
    • WorkflowParser
    • ProviderParser
    • ActionParser
  2. Create a facade class to coordinate these parsers
  3. Use dependency injection for better testability

This refactoring would:

  • Improve maintainability
  • Make the code easier to test
  • Reduce cognitive complexity
  • Make it easier to add new parsing features

Line range hint 644-644: Address TODO comment for provider ID extraction

The TODO comment "FIX THIS SHIT" is not descriptive and indicates a known issue with the provider ID extraction logic.

Would you like me to help implement a more robust provider ID extraction method? I can:

  1. Implement proper regex-based parsing
  2. Add comprehensive validation
  3. Create appropriate error messages
  4. Add unit tests

Let me know if you'd like me to open a GitHub issue to track this improvement.

keep/providers/newrelic_provider/newrelic_provider.py (2)

41-47: Enhance URL validation with additional constraints

While the switch to HttpsUrl type is good, consider adding these additional validations for the New Relic API URL:

  1. Maximum length constraint
  2. Allowed domains validation
     new_relic_api_url: HttpsUrl = dataclasses.field(
         metadata={
             "required": False,
             "description": "New Relic API URL",
-            "validation": "https_url"
+            "validation": "https_url",
+            "max_length": 255,
+            "allowed_domains": ["newrelic.com", "eu.newrelic.com"]
         },
         default="https://api.newrelic.com",
     )

Line range hint 1-600: Consider implementing rate limiting for API calls

The code makes multiple API calls to New Relic's GraphQL endpoint but doesn't implement rate limiting. This could lead to hitting API rate limits during high-frequency operations.

Consider implementing a rate limiter using a token bucket algorithm or similar approach to manage API request rates. This would help prevent hitting New Relic's API rate limits and ensure stable operation.

Example implementation approach:

  1. Add a rate limiter utility class
  2. Wrap API calls with rate limiting logic
  3. Make rate limits configurable via provider config
keep/providers/datadog_provider/datadog_provider.py (4)

16-17: Consider consistent line wrapping for imports

The import statements have inconsistent line wrapping and indentation. Consider using parentheses for cleaner multi-line imports.

-from datadog_api_client.exceptions import (ApiException, ForbiddenException,
-                                           NotFoundException)
+from datadog_api_client.exceptions import (
+    ApiException,
+    ForbiddenException,
+    NotFoundException,
+)

-from datadog_api_client.v1.api.webhooks_integration_api import \
-    WebhooksIntegrationApi
+from datadog_api_client.v1.api.webhooks_integration_api import (
+    WebhooksIntegrationApi,
+)

-from datadog_api_client.v2.api.service_definition_api import \
-    ServiceDefinitionApi
+from datadog_api_client.v2.api.service_definition_api import (
+    ServiceDefinitionApi,
+)

Also applies to: 22-23, 28-29


Line range hint 673-674: Move imports to the top of the file

The random and hashlib imports should be moved to the top of the file with other imports.

+import hashlib
+import random

 class DatadogProvider(BaseTopologyProvider):
     """Pull/push alerts from Datadog."""
-    # Choose a random alert type
-    import hashlib
-    import random

Line range hint 724-761: Consider extracting service processing logic

The pull_topology method contains complex nested data processing logic. Consider extracting the service processing into a separate method for better maintainability and testability.

def _process_service_definition(self, service_definition, environment: str) -> TopologyServiceInDto:
    """Process a single service definition and return a TopologyServiceInDto."""
    name = service_definition.attributes.schema.info.dd_service
    return TopologyServiceInDto(
        source_provider_id=self.provider_id,
        repository=service_definition.attributes.schema.integrations.github,
        tags=service_definition.attributes.schema.tags,
        service=name,
        display_name=service_definition.attributes.schema.info.display_name,
        environment=environment,
        description=service_definition.attributes.schema.info.description,
        team=service_definition.attributes.schema.org.team,
        application=service_definition.attributes.schema.org.application,
        email=service_definition.attributes.schema.contact.email,
        slack=service_definition.attributes.schema.contact.slack,
    )

Line range hint 191-193: Add @staticmethod decorator

The convert_to_seconds method should be decorated with @staticmethod since it doesn't use any instance attributes.

+    @staticmethod
     def convert_to_seconds(s):
         seconds_per_unit = {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800}
         return int(s[:-1]) * seconds_per_unit[s[-1]]
keep/providers/centreon_provider/centreon_provider.py (3)

88-89: Use urllib.parse.urljoin for safe URL construction

Concatenating URLs using string operations can lead to incorrect URLs, especially if the host_url does not end with a slash or if params starts with a slash. Using urllib.parse.urljoin ensures that the URL is constructed correctly and handles edge cases gracefully.

Apply this diff to implement safe URL construction:

+import urllib.parse

 def __get_url(self, params: str):
-    url = self.authentication_config.host_url + "/centreon/api/index.php?" + params
+    base_url = urllib.parse.urljoin(
+        self.authentication_config.host_url, "/centreon/api/index.php"
+    )
+    url = f"{base_url}?{params}"
     return url

122-122: Set timeouts for HTTP requests to prevent hanging

The requests.get calls do not specify a timeout, which can cause the application to hang indefinitely if the Centreon API becomes unresponsive. Specifying a timeout ensures that your application can handle such scenarios gracefully by raising a timeout exception after a specified period.

Apply this diff to add a timeout to your HTTP requests:

 response = requests.get(url, headers=self.__get_headers())
+                       timeout=10)

You may adjust the timeout duration (10 seconds in this example) as appropriate for your application's requirements.

Also applies to: 156-156


119-152: Refactor duplicate code in __get_host_status and __get_service_status

Both methods share similar logic for making API requests and processing responses. Refactoring the common functionality into a helper method can reduce code duplication and improve maintainability.

Consider creating a generic method like __get_status:

def __get_status(self, object_name: str, id_field: str) -> list[AlertDto]:
    # Shared logic for fetching and parsing status

Then modify __get_host_status and __get_service_status to utilize this helper method.

Also applies to: 153-185

🧰 Tools
🪛 Ruff

151-151: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

keep/providers/base/base_provider.py (1)

334-335: Consider moving the import to the top level to enhance code clarity

Importing ProvidersFactory within the format_alert method may be necessary to prevent circular dependencies. However, if possible, consider reorganizing the code to import it at the module level to improve readability and maintainability.

keep-ui/app/providers/provider-form.tsx (2)

327-331: Avoid assignments within expressions to enhance readability.

Assigning values within a ternary expression can be confusing and reduce code clarity. It's better to separate the assignment from the expression.

Refactor the code as follows:

 Object.entries(validation.error.format()).forEach(([field, err]) => {
-  err && typeof err === "object" && !Array.isArray(err)
-    ? (errors[field] = err._errors[0])
-    : null;
+  if (err && typeof err === "object" && !Array.isArray(err)) {
+    errors[field] = err._errors[0];
+  }
 });
🧰 Tools
🪛 Biome

[error] 329-329: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


1085-1087: Simplify null checks using optional chaining.

You can streamline the null checks by utilizing optional chaining, which improves code conciseness.

Apply this diff to refactor the code:

 function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
-  if (e.target.files && e.target.files[0]) {
+  if (e.target.files?.[0]) {
     setSelected(e.target.files[0].name);
   }
   onChange(e);
🧰 Tools
🪛 Biome

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

keep-ui/app/providers/form-validation.ts (2)

35-35: Typo in error message: "protocol-relavie" should be "protocol-relative"

In line 35, there's a typo in the error message for relProtocolError. The word "relavie" should be "relative".

Apply this diff to correct the typo:

-const relProtocolError = error("A protocol-relavie URL is not allowed");
+const relProtocolError = error("A protocol-relative URL is not allowed");

108-177: Consider using the built-in URL class for URL parsing and validation

The custom isURL function implements complex URL parsing logic, which can be error-prone and hard to maintain. Consider utilizing the built-in URL class for robust and standard-compliant URL parsing.

Here's how you might refactor the isURL function:

function isURL(str: string, options?: Partial<URLOptions>): ValidatorRes {
  const opts = mergeOptions(defaultURLOptions, options);
  try {
    const url = new URL(str);

    // Validate protocol
    const protocol = url.protocol.slice(0, -1); // Remove the trailing ':'
    if (
      opts.protocols.length &&
      !opts.protocols.includes(protocol.toLowerCase())
    ) {
      return getProtocolError(opts.protocols);
    }

    // Validate port
    if (opts.requirePort && !url.port) {
      return missingPortError;
    }
    if (url.port) {
      const port = parseInt(url.port, 10);
      if (port <= 0 || port > 65_535) return portError;
    }

    // Validate host
    const host = url.hostname;
    if (!host) return hostError;
    if (isIP(host)) return { success: true };
    const fqdnValidation = isFQDN(host, opts);
    if (!fqdnValidation.success) return fqdnValidation;

    return { success: true };
  } catch {
    return urlError;
  }
}

This approach leverages the standard URL parsing, reducing the complexity and potential bugs in custom parsing logic.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0ed6c9 and b9d0f4b.

⛔ Files ignored due to path filters (1)
  • keep-ui/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (61)
  • keep-ui/app/providers/form-validation.ts (1 hunks)
  • keep-ui/app/providers/provider-form-scopes.tsx (3 hunks)
  • keep-ui/app/providers/provider-form.tsx (21 hunks)
  • keep-ui/app/providers/providers-tiles.tsx (1 hunks)
  • keep-ui/app/providers/providers.tsx (1 hunks)
  • keep-ui/app/workflows/workflow-tile.tsx (0 hunks)
  • keep-ui/package.json (1 hunks)
  • keep/parser/parser.py (1 hunks)
  • keep/providers/appdynamics_provider/appdynamics_provider.py (1 hunks)
  • keep/providers/auth0_provider/auth0_provider.py (5 hunks)
  • keep/providers/base/base_provider.py (3 hunks)
  • keep/providers/bigquery_provider/bigquery_provider.py (1 hunks)
  • keep/providers/centreon_provider/centreon_provider.py (1 hunks)
  • keep/providers/cilium_provider/cilium_provider.py (2 hunks)
  • keep/providers/clickhouse_provider/clickhouse_provider.py (6 hunks)
  • keep/providers/datadog_provider/datadog_provider.py (2 hunks)
  • keep/providers/discord_provider/discord_provider.py (1 hunks)
  • keep/providers/elastic_provider/elastic_provider.py (4 hunks)
  • keep/providers/gcpmonitoring_provider/gcpmonitoring_provider.py (1 hunks)
  • keep/providers/gitlab_provider/gitlab_provider.py (1 hunks)
  • keep/providers/gke_provider/gke_provider.py (1 hunks)
  • keep/providers/google_chat_provider/google_chat_provider.py (2 hunks)
  • keep/providers/grafana_incident_provider/grafana_incident_provider.py (6 hunks)
  • keep/providers/grafana_oncall_provider/grafana_oncall_provider.py (2 hunks)
  • keep/providers/grafana_provider/grafana_provider.py (2 hunks)
  • keep/providers/graylog_provider/graylog_provider.py (3 hunks)
  • keep/providers/ilert_provider/ilert_provider.py (3 hunks)
  • keep/providers/jira_provider/jira_provider.py (4 hunks)
  • keep/providers/jiraonprem_provider/jiraonprem_provider.py (1 hunks)
  • keep/providers/kafka_provider/kafka_provider.py (1 hunks)
  • keep/providers/kibana_provider/kibana_provider.py (4 hunks)
  • keep/providers/kubernetes_provider/kubernetes_provider.py (2 hunks)
  • keep/providers/mattermost_provider/mattermost_provider.py (2 hunks)
  • keep/providers/mongodb_provider/mongodb_provider.py (4 hunks)
  • keep/providers/mysql_provider/mysql_provider.py (1 hunks)
  • keep/providers/newrelic_provider/newrelic_provider.py (3 hunks)
  • keep/providers/ntfy_provider/ntfy_provider.py (1 hunks)
  • keep/providers/openobserve_provider/openobserve_provider.py (3 hunks)
  • keep/providers/openshift_provider/openshift_provider.py (3 hunks)
  • keep/providers/opsgenie_provider/opsgenie_provider.py (1 hunks)
  • keep/providers/postgres_provider/postgres_provider.py (4 hunks)
  • keep/providers/prometheus_provider/prometheus_provider.py (1 hunks)
  • keep/providers/redmine_provider/redmine_provider.py (5 hunks)
  • keep/providers/sentry_provider/sentry_provider.py (2 hunks)
  • keep/providers/servicenow_provider/servicenow_provider.py (1 hunks)
  • keep/providers/site24x7_provider/site24x7_provider.py (2 hunks)
  • keep/providers/slack_provider/slack_provider.py (1 hunks)
  • keep/providers/smtp_provider/smtp_provider.py (1 hunks)
  • keep/providers/splunk_provider/splunk_provider.py (4 hunks)
  • keep/providers/squadcast_provider/squadcast_provider.py (4 hunks)
  • keep/providers/ssh_provider/ssh_provider.py (2 hunks)
  • keep/providers/teams_provider/teams_provider.py (1 hunks)
  • keep/providers/uptimekuma_provider/uptimekuma_provider.py (1 hunks)
  • keep/providers/victoriametrics_provider/victoriametrics_provider.py (2 hunks)
  • keep/providers/webhook_provider/webhook_provider.py (1 hunks)
  • keep/providers/zabbix_provider/zabbix_provider.py (1 hunks)
  • keep/validation/fields.py (1 hunks)
  • keep_clickhouse_e53b19ac051549aeb333e9c07cb25db8 (1 hunks)
  • keep_prometheus_06e9495272b34630b4170634336ba8e1 (1 hunks)
  • keep_victoriametrics_c77ff2c5e09d4ec38335f752cd4f8a39 (1 hunks)
  • tests/e2e_tests/test_end_to_end.py (3 hunks)
💤 Files with no reviewable changes (1)
  • keep-ui/app/workflows/workflow-tile.tsx
✅ Files skipped from review due to trivial changes (5)
  • keep/providers/gcpmonitoring_provider/gcpmonitoring_provider.py
  • keep/providers/site24x7_provider/site24x7_provider.py
  • keep_clickhouse_e53b19ac051549aeb333e9c07cb25db8
  • keep_prometheus_06e9495272b34630b4170634336ba8e1
  • keep_victoriametrics_c77ff2c5e09d4ec38335f752cd4f8a39
🧰 Additional context used
🪛 Biome
keep-ui/app/providers/provider-form.tsx

[error] 106-106: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 115-115: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 329-329: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 Ruff
keep/providers/centreon_provider/centreon_provider.py

151-151: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


184-184: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

keep/providers/grafana_incident_provider/grafana_incident_provider.py

181-181: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

keep/providers/redmine_provider/redmine_provider.py

182-182: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (83)
keep/providers/google_chat_provider/google_chat_provider.py (3)

1-11: LGTM! Imports are properly structured

The addition of HttpsUrl from the validation fields module aligns with the PR's objective to enhance type safety.


Line range hint 40-43: LGTM! Validation leverages Pydantic's type system

The simplified validate_config method properly utilizes Pydantic's built-in validation through the GoogleChatProviderAuthConfig class.


18-24: Verify webhook URL pattern compatibility with test environments

The switch to HttpsUrl enforces HTTPS protocol, which improves security but might affect development/testing scenarios.

✅ Verification successful

Let me gather more information about test files and webhook URL patterns.


Let me try one more search to specifically look for Google Chat webhook URL patterns in test files with a corrected regex.


Based on the results from all our searches, I can now provide the final response:

HTTPS URL validation is appropriate for Google Chat webhook URLs

The codebase analysis shows:

  • No test cases are currently using non-HTTPS URLs for Google Chat webhooks
  • The e2e tests demonstrate proper HTTPS URL validation for similar webhook providers (Teams)
  • Google Chat webhooks are exclusively HTTPS in production environments

The switch to HttpsUrl type is safe and aligns with Google Chat's security requirements, as there's no evidence of HTTP usage in development or testing scenarios.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for webhook URL patterns in test files
echo "Searching for webhook URL patterns in tests..."
rg -g "test_*.py" "GOOGLE_CHAT_WEBHOOK_URL.*=.*" -A 2

# Check for environment variable usage
echo "Checking environment variable usage..."
rg "GOOGLE_CHAT_WEBHOOK_URL" -A 2

Length of output: 582


Script:

#!/bin/bash
# Search for test files related to Google Chat provider
echo "Searching for Google Chat provider test files..."
fd "test.*google.*chat.*\.py$"

# Check for any HTTP/HTTPS URL patterns in test files
echo "Checking URL patterns in test files..."
rg -g "test_*.py" "(http|https)://" -A 2

# Look for mock or fixture data for webhook URLs
echo "Checking for mock/fixture data..."
rg -g "test_*.py" "(mock|fixture).*webhook" -A 2

# Check for environment variable setup in test files
echo "Checking test environment setup..."
rg -g "test_*.py" "environ\[(\"|\')GOOGLE_CHAT_WEBHOOK_URL(\"|\')]" -A 2

Length of output: 9113


Script:

#!/bin/bash
# Search for Google Chat webhook patterns in test files
echo "Searching for Google Chat webhook patterns..."
rg -g "test_*.py" "webhook.*google.*chat|google.*chat.*webhook" -B 2 -A 2

# Check for environment variables in test setup with fixed regex
echo "Checking environment variables in tests..."
rg -g "test_*.py" "environ.*(GOOGLE_CHAT|webhook)" -B 2 -A 2

# Look for Google Chat provider test setup
echo "Looking for Google Chat provider setup..."
rg -g "test_*.py" "google.*chat.*provider" -i -B 2 -A 2

Length of output: 485

keep/providers/mattermost_provider/mattermost_provider.py (1)

16-22: 🛠️ Refactor suggestion

Consider using stricter URL validation for webhook_url

While pydantic.AnyHttpUrl provides basic URL validation, consider using a more restrictive type for enhanced security:

  1. Restrict to HTTPS-only URLs (except for development)
  2. Add max length validation
  3. Consider blocking internal/localhost URLs in production
-    webhook_url: pydantic.AnyHttpUrl = dataclasses.field(
+    webhook_url: pydantic.HttpsUrl = dataclasses.field(
         metadata={
             "required": True,
             "description": "Mattermost Webhook Url",
             "sensitive": True,
-            "validation": "any_http_url",
+            "validation": "https_url",
+            "max_length": 2048,
         }
     )
keep-ui/app/providers/providers.tsx (2)

20-21: Verify group hierarchy implementation and add documentation.

The new grouping properties allow for better organization, but please ensure:

  1. Sub-groups are always associated with valid main groups
  2. The UI correctly handles these groupings

Let's check the implementation:

✅ Verification successful

Based on the code analysis, I can now provide a final response regarding the group hierarchy implementation:

Group hierarchy is properly implemented with validation and UI handling

The implementation shows:

  1. Groups are properly organized through getConfigGroup function that handles both main and sub-groups
  2. Sub-groups are always rendered within their parent main group using a tab interface
  3. Form validation is handled through Zod schema (getZodSchema)
  4. UI clearly separates and organizes fields by their groups using Cards and TabGroups components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify group hierarchy implementation
# Expected: Find UI components that render these groups and their relationship

# Search for group-related rendering logic
rg -t typescript -t javascript "config_(main|sub)_group"

# Search for group-related tests
rg -t typescript -t javascript "describe.*config.*group" -A 10

Length of output: 193


Script:

#!/bin/bash
# Let's try with ripgrep without file type filters
# Search for group-related rendering logic and implementation
rg "config_(main|sub)_group"

# Search for files that might handle configuration groups
rg -l "(main|sub).*group.*config"

# Look for any UI components that might render configuration
fd -e tsx -e ts | xargs rg "render.*config"

Length of output: 2985


Script:

#!/bin/bash
# Let's examine the provider-form.tsx implementation to understand how groups are handled
cat keep-ui/app/providers/provider-form.tsx

# Also check if there are any validation functions
rg -A 5 "validate.*config" keep-ui/app/providers/

Length of output: 36558


5-11: LGTM! Well-defined validation types for URLs and related inputs.

The validation types are comprehensive and align well with the PR objectives for enhanced input validation.

Let's verify the consistent usage of these validation types across the codebase:

✅ Verification successful

Based on the search results, I found that the validation types are implemented in keep-ui/app/providers/form-validation.ts, which shows the validation logic for each type:

    if (config.validation === "any_url") {
    if (config.validation === "any_http_url") {
    if (config.validation === "https_url") {
    if (config.validation === "no_scheme_url") {
    if (config.validation === "tld") {
    if (config.validation === "port") {

Let me verify the implementation details:


Based on the implementation in form-validation.ts, I can now provide a final response:

The validation types are well-implemented with comprehensive URL validation logic

The validation types are properly implemented with thorough validation logic:

  • any_url: Basic URL validation with configurable options
  • any_http_url: Enforces http/https protocols
  • https_url: Strictly enforces https protocol with TLD requirement and max length of 2083
  • no_scheme_url: Allows URLs without protocol specification
  • tld: Validates top-level domains with regex pattern
  • port: Validates port numbers within range 1-65535

The implementation includes proper error handling, comprehensive URL parsing, and validation for various URL components including protocols, IPv6 addresses, ports, and hostnames.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of validation types in provider configurations
# Expected: All provider configurations should use these validation types

# Search for validation type usage in provider configurations
rg -t typescript -t javascript '"validation":\s*"(any_url|any_http_url|https_url|no_scheme_url|port|tld)"'

Length of output: 143


Script:

#!/bin/bash
# Let's try a different approach to search for validation types usage
# First, let's find all TypeScript/JavaScript files
fd "\.(ts|tsx|js|jsx)$" | while read -r file; do
    echo "=== $file ==="
    # Search for validation types in each file
    rg "validation.*(?:any_url|any_http_url|https_url|no_scheme_url|port|tld)" "$file" || true
done

# Also search for any validation-related configuration or schema definitions
rg -g "*.{ts,tsx,js,jsx}" "validation\s*[=:]\s*['\"]" -A 2 -B 2

Length of output: 103608


Script:

#!/bin/bash
# Check the validation implementation
echo "=== Validation Implementation ==="
cat keep-ui/app/providers/form-validation.ts

Length of output: 9356

keep/providers/teams_provider/teams_provider.py (2)

13-13: LGTM: Clean import addition

The HttpsUrl import is properly placed and aligns with the PR's objective of enhancing type safety.


20-27: Excellent enhancement of webhook URL validation!

The change from str to HttpsUrl with proper metadata improves type safety and validation. The sensitive flag is a good security practice for webhook URLs.

Let's verify the impact on existing configurations:

✅ Verification successful

Type change to HttpsUrl appears safe to implement

The verification shows:

  • No instances of non-HTTPS Teams webhook URLs in tests or configurations
  • The documentation needs to be updated as it still shows webhook_url as type str
  • All found webhook URL examples are either environment variables or HTTPS URLs

The change to enforce HTTPS URLs is safe to implement as there's no evidence of HTTP usage in the codebase. However, the documentation in docs/providers/documentation/teams-provider.mdx should be updated to reflect the new type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Teams webhook URL patterns in test files and configurations
# to ensure they comply with the new HTTPS requirement

echo "Checking test files for webhook URL patterns:"
rg -l "TEAMS_WEBHOOK_URL.*=.*['\"]http://" 

echo "Checking configuration files for non-HTTPS Teams webhook URLs:"
rg -l "webhook_url.*['\"]http://" --type yaml --type json

Length of output: 344


Script:

#!/bin/bash
# Let's check for any existing webhook URL patterns in tests and configurations
# This time with a broader search to see all URL patterns

echo "Checking test files for Teams webhook URL patterns:"
rg -A 2 "TEAMS_WEBHOOK_URL.*=" 

echo "Checking configuration files for Teams webhook URLs:"
rg -A 2 "webhook_url.*:" --type yaml --type json

echo "Checking test files for Teams provider configuration:"
rg -A 5 "TeamsProviderAuthConfig" 

Length of output: 2166

keep-ui/package.json (2)

102-102: LGTM! Good choice for schema validation.

The addition of Zod is appropriate for implementing robust input validation, and it integrates well with your existing TypeScript and react-hook-form setup.


102-102: Verify Zod version for security vulnerabilities.

Let's verify if version 3.23.8 has any known security issues.

✅ Verification successful

Zod version 3.23.8 is secure and up to date

The current version (3.23.8) is:

  • The latest available version from npm
  • Not affected by the known DoS vulnerability (CVE fixed in 3.22.3)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security advisories and latest versions of the zod package

# Check npm for latest versions
echo "Latest version from npm:"
npm view zod version

# Check for security advisories
echo -e "\nSecurity advisories from GitHub:"
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "zod") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 710

keep/providers/mysql_provider/mysql_provider.py (2)

Line range hint 82-91: Verify MySQL connector compatibility with URL-formatted hosts

The mysql.connector.connect method expects different host formats based on the connection type. Using pydantic.AnyUrl might require parsing before passing to the connector.

✅ Verification successful

Let me generate additional scripts to verify the MySQL host handling implementation.


Based on the search results, I can now provide a final response:

MySQL host validation is correctly implemented with pydantic.AnyUrl

The MySQL provider's authentication configuration correctly uses pydantic.AnyUrl for host validation, which is the appropriate choice for MySQL connections as it:

  • Accepts both URL-formatted and plain hostname inputs
  • Handles host-only formats (e.g., "localhost") and URL formats (e.g., "mysql://hostname")
  • Is less restrictive than AnyHttpUrl which is used by HTTP-based providers

The MySQL connector is compatible with both formats and will correctly parse the host value from the URL if provided.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for MySQL connection patterns in tests
echo "Searching for MySQL connection patterns in tests..."
rg -A 5 'mysql\.connector\.connect' --type py --glob '*test*.py'

# Search for connection handling in similar providers
echo "Searching for host handling in other providers..."
rg -A 5 'host.*=.*authentication_config\.host' --type py

Length of output: 9106


Script:

#!/bin/bash
# Check MySQL authentication config and validation
echo "Checking MySQL authentication config..."
rg -A 10 'class .*MySQLAuthenticationConfig' --type py

# Check MySQL provider initialization and validation
echo "Checking MySQL provider initialization..."
rg -A 10 'class .*MySQLProvider' --type py

# Look for any URL validation or parsing
echo "Checking URL validation..."
rg -A 5 'AnyUrl|HttpUrl|parse_url|urlparse' --type py

Length of output: 24512


24-29: Verify the impact on existing configurations

The stricter validation might break existing valid configurations. Let's verify the current host values in use.

✅ Verification successful

The previous search found some MySQL host configurations, but let's gather more context about how the MySQL provider is configured in tests and actual usage to better understand the impact of the stricter validation.


Let's gather more information about how the MySQL provider is used in tests and documentation to understand the impact of the URL validation.


Based on the search results from all the scripts executed, I can now provide a final response:

The stricter URL validation appears safe to implement

From the codebase analysis:

  • The only MySQL host configuration found is through environment variable MYSQL_HOST in mysql_provider.py
  • Test configurations use simple localhost URLs (e.g., "localhost:3306")
  • No complex or potentially incompatible host values were found in the codebase or documentation

The change to pydantic.AnyUrl validation should not break existing configurations as:

  1. Environment variable-based configuration allows for standard host formats
  2. Test cases use standard localhost URLs that are compatible with URL validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for MySQL host configurations in the codebase
echo "Searching for MySQL host configurations in code..."
rg -A 1 'MYSQL_HOST|mysql.*host' --type py

echo "Searching for MySQL connection examples in documentation..."
rg -A 1 'mysql.*host|MYSQL_HOST' --type md --type txt

Length of output: 902


Script:

#!/bin/bash
# Search for MySQL configuration examples in test files and configuration files
echo "Searching for MySQL configuration examples in test files..."
rg -A 5 'mysql_provider.*config' --type py

echo "Searching for MySQL environment variable usage..."
rg -B 2 -A 2 'MYSQL_HOST' --type py

echo "Searching for MySQL connection strings or URLs..."
rg -A 1 'mysql[+].*:\/\/' --type py --type yaml --type env

Length of output: 1377


Script:

#!/bin/bash
# Search for MySQL connection strings with proper escape sequence
echo "Searching for MySQL connection strings..."
rg -A 1 "mysql\+" --type py --type yaml --type env

# Search for test configurations of MySQL provider
echo "Searching for MySQL provider test configurations..."
fd -e py -x grep -A 10 "class.*MysqlProviderTest" {}

# Search for example configurations in documentation
echo "Searching for MySQL configuration examples in docs..."
fd -e md -e rst -x grep -A 5 -i "mysql.*configuration" {}

Length of output: 522

keep-ui/app/providers/provider-form-scopes.tsx (1)

26-26: Props refactoring improves type safety!

Good improvements in the component interface:

  • Replacing triggerRevalidateScope: any with onRevalidate: () => void provides better type safety
  • Using provider.installed directly instead of a separate prop reduces prop drilling

Also applies to: 31-31

keep/providers/discord_provider/discord_provider.py (2)

14-14: LGTM: Import of validation field type

The addition of HttpsUrl import aligns with the PR's objective to enhance input validation for provider configurations.


21-27: LGTM: Enhanced webhook URL validation

The change from str to HttpsUrl with explicit validation metadata improves type safety and ensures only HTTPS URLs are accepted.

Let's verify the error handling for invalid URLs:

✅ Verification successful

LGTM: Webhook URL validation and error handling are properly implemented

The change from str to HttpsUrl with validation metadata is well implemented with proper error handling:

  1. Input validation:

    • Uses HttpsUrl type with "https_url" validation
    • Marked as required and sensitive
    • Validated during config initialization
  2. Error handling in notify method:

    • Validates required parameters (content/components)
    • Handles HTTP response codes
    • Detailed error messages for different failure scenarios
    • Proper exception handling for JSON parsing
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check error handling implementation for invalid webhook URLs

# Search for error handling related to webhook URL validation
rg -A 5 "webhook.*url" --glob "!*.pyc"

# Look for related test cases
rg -l "test.*webhook.*url|webhook.*url.*test" --glob "**/*test*.py"

Length of output: 194165


Script:

#!/bin/bash
# Let's check for error handling implementation in the discord provider
ast-grep --pattern $'class DiscordProvider {
  $$$
  notify($$$) {
    $$$
  }
  $$$
}'

# Let's also check for validation tests
rg -A 5 "def test.*discord.*webhook" --glob "**/*test*.py"

Length of output: 149


Script:

#!/bin/bash
# Let's check the discord provider implementation for error handling
rg -A 10 "class DiscordProvider" --glob "**/*.py"

# Also check for any validation related code
rg -A 5 "HttpsUrl.*webhook" --glob "**/*.py"

Length of output: 1885


Script:

#!/bin/bash
# Let's check the notify method implementation for error handling
rg -B 5 -A 15 "def notify" keep/providers/discord_provider/discord_provider.py

# Also check the validation_config method
rg -B 2 -A 10 "def validate_config" keep/providers/discord_provider/discord_provider.py

Length of output: 502


Script:

#!/bin/bash
# Let's check the entire discord provider file content
cat keep/providers/discord_provider/discord_provider.py

# And check for any base provider validation code
rg -A 10 "class BaseProvider" --glob "**/*.py"

Length of output: 5384

keep/providers/auth0_provider/auth0_provider.py (2)

32-38: LGTM! Token field metadata improvements are helpful.

The updated description and hint provide clear guidance for users to locate their Auth0 API token.


14-14: LGTM! Domain field validation enhancement looks good.

The transition to HttpsUrl type with detailed metadata improves type safety and user guidance. The validation rule ensures proper HTTPS URL format.

Let's verify the HttpsUrl implementation:

Also applies to: 23-29

✅ Verification successful

HttpsUrl validation is consistently implemented across providers

The verification shows that:

  • HttpsUrl is properly implemented in keep/validation/fields.py as a subclass of HttpUrl that strictly enforces HTTPS scheme
  • The type is consistently used across multiple providers for URL validation:
    • Auth0: domain field
    • Datadog: domain field
    • ServiceNow: service_now_base_url field
    • Teams, Slack, Discord: webhook_url field
    • And several other providers

The implementation aligns with security best practices by enforcing HTTPS-only URLs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if HttpsUrl is consistently used across other providers
ast-grep --pattern 'domain: HttpsUrl = dataclasses.field'

Length of output: 59


Script:

#!/bin/bash
# Let's try a broader search for HttpsUrl usage
rg "HttpsUrl" -A 3 -B 3

# Also check for domain field definitions across providers
ast-grep --pattern 'domain: $_ = dataclasses.field'

# And check the HttpsUrl implementation
cat keep/validation/fields.py

Length of output: 18277

keep/providers/bigquery_provider/bigquery_provider.py (1)

29-29: LGTM! The MIME type change improves precision.

The change from ".json" to "application/json" is a good improvement as it more accurately specifies the expected file format using standard MIME type notation.

Let's verify consistency across other providers:

✅ Verification successful

MIME type is consistently used across providers

The verification confirms that all GCP-related providers (gke_provider.py and gcpmonitoring_provider.py) consistently use the MIME type "application/json" for JSON file inputs. This change aligns the BigQuery provider with the existing standard in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if other providers use consistent MIME types for JSON files
# Expected: All JSON file inputs should use "application/json" MIME type

rg -A 3 'file_type.*json' --glob '!keep/providers/bigquery_provider/**'

Length of output: 724

keep/providers/clickhouse_provider/clickhouse_provider.py (4)

15-15: LGTM: Import of custom validation types

The addition of custom validation types aligns with the PR's objective of enhancing input validation.


24-28: LGTM: Enhanced security metadata

Good addition of the sensitive flag for the password field, which helps in proper handling of sensitive data.


76-79: LGTM: Method improvements and proper use of typed fields

The changes improve code readability while maintaining functionality:

  • Proper whitespace around SQL execution
  • Clear variable assignments using typed fields
  • Consistent method signature formatting

Also applies to: 104-108, 137-137


30-42: Verify validation behavior for host and port fields

The type changes from str to NoSchemeUrl and UrlPort look good and align with the PR objectives. Let's verify the validation behavior:

✅ Verification successful

Validation implementation for host and port fields is properly implemented

The validation for both fields is well-implemented and secure:

  • UrlPort is defined as a constrained integer (using pydantic's conint) with valid port range 1-65535
  • NoSchemeUrl extends pydantic's AnyUrl with custom build logic to handle URLs without schemes
  • Both types are used consistently across multiple providers (SSH, Splunk, SMTP, Postgres, etc.)
  • End-to-end tests verify the validation behavior for both field types:
    • Port validation tests check for invalid ports and out-of-range values (0)
    • NoSchemeUrl validation tests verify that URLs with/without schemes are properly handled

The type changes in the Clickhouse provider align with the established validation patterns in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the validation behavior of NoSchemeUrl and UrlPort types
# Look for test files that cover these validations

# Search for test files related to these fields
echo "Searching for related test files..."
fd -e py -e pytest test_clickhouse

# Search for validation test cases
echo "Searching for validation test cases..."
rg -A 5 "NoSchemeUrl|UrlPort" --type py

# Look for any existing validation error handling
echo "Searching for validation error handling..."
rg -A 5 "ValidationError.*clickhouse" --type py

Length of output: 11614


Script:

#!/bin/bash
# Let's check the validation implementation and test coverage

# Check the NoSchemeUrl implementation
echo "Checking NoSchemeUrl implementation..."
ast-grep --pattern 'class NoSchemeUrl { $$$ }'

# Check the UrlPort definition and usage
echo "Looking for UrlPort validation..."
rg -A 2 "UrlPort = " --type py

# Look for test files
echo "Looking for validation tests..."
fd -e py test_validation
fd -e py test_fields

# Check for any validation tests in the test files
echo "Checking validation test content..."
rg -A 5 "test.*NoSchemeUrl|test.*UrlPort" --type py

Length of output: 2502


Script:

#!/bin/bash
# Let's check the actual validation implementation and any test files

# Check the NoSchemeUrl class implementation
echo "Checking NoSchemeUrl class implementation..."
rg -A 10 "class NoSchemeUrl" --type py

# Check for validation tests in tests directory
echo "Looking for validation tests in tests directory..."
fd -e py -d 1 . tests/
rg -A 5 "test.*validation" tests/ || true

# Check for pydantic validation usage
echo "Checking pydantic validation usage..."
rg -A 5 "from pydantic.*validation" --type py
rg -A 5 "@validator.*NoSchemeUrl|@validator.*UrlPort" --type py

# Check for any validation error handling in providers
echo "Checking validation error handling in providers..."
rg -A 5 "ValidationError.*host|ValidationError.*port" keep/providers/ || true

Length of output: 5670

keep/providers/ssh_provider/ssh_provider.py (3)

16-16: LGTM! Import of validation types aligns with PR objectives

The addition of NoSchemeUrl and UrlPort imports supports the enhanced input validation requirements.


23-28: Verify validation behavior for edge cases

The type changes from str to NoSchemeUrl for host and int to UrlPort for port enhance input validation. Let's verify the validation behavior:

Also applies to: 33-35


44-45: Verify SSH key format support and security implications

The expanded file type support now includes various SSH key formats. However, we should verify:

  1. Coverage of all common SSH key formats (RSA, DSA, ECDSA, Ed25519)
  2. Security implications of accepting application/octet-stream
keep/providers/postgres_provider/postgres_provider.py (3)

14-14: LGTM: Validation fields import added correctly

The addition of custom validation fields aligns with the PR's objective to enhance input validation and type safety.


29-34: LGTM: Enhanced type safety for host field

The change from str to NoSchemeUrl with proper validation metadata improves type safety and input validation.


40-46: Verify localhost and protocol handling

While the type safety improvements are good, please verify:

  1. How does NoSchemeUrl handle localhost connections?
  2. Are there any implications for existing connections using localhost?
✅ Verification successful

Let me gather more specific information about URL handling in the codebase.


Let me check the actual implementation of NoSchemeUrl to understand its localhost handling.


Based on the gathered information, I can now provide a final response about the localhost and protocol handling concerns.

NoSchemeUrl safely handles localhost connections

The implementation of NoSchemeUrl is a subclass of AnyUrl that simply strips the scheme from the URL while maintaining all standard URL validation. This means:

  • Localhost connections are handled exactly like any other host
  • The class inherits Pydantic's robust URL validation
  • It preserves all URL components (user, password, host, port, path, query, fragment) while only removing the scheme

Looking at the usage across providers:

  • Splunk provider explicitly supports localhost as default value
  • Other providers (Postgres, Clickhouse, SSH) handle localhost identically to any other host
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing localhost usage patterns
rg -l 'localhost' --type py

# Check NoSchemeUrl implementation for localhost handling
ast-grep --pattern 'class NoSchemeUrl {
  $$$
}'

Length of output: 1273


Script:

#!/bin/bash
# Search for NoSchemeUrl usage and implementation
rg -A 10 "NoSchemeUrl" --type py

# Search for URL validation and localhost handling patterns
rg -A 5 "def.*validate.*url" --type py
rg -A 5 "localhost.*validation" --type py

# Look for URL-related tests
fd "test.*url" -e py --exec cat {}

Length of output: 9768


Script:

#!/bin/bash
# Get the full implementation of NoSchemeUrl class
rg -A 30 "^class NoSchemeUrl" --type py

# Check for any URL-related tests specifically for localhost scenarios
rg -A 5 "localhost.*test" --type py
rg -A 5 "test.*localhost" --type py

Length of output: 2502

keep/providers/cilium_provider/cilium_provider.py (1)

10-11: LGTM!

The import statement is correctly formatted and maintains the required functionality.

keep/providers/uptimekuma_provider/uptimekuma_provider.py (1)

22-28: Verify UptimeKuma API compatibility with AnyHttpUrl.

The change to pydantic.AnyHttpUrl improves type safety and validation. However, we should verify that this works correctly with the UptimeKuma API client.

Also, consider adding custom validation to ensure UptimeKuma-specific URL requirements:

@pydantic.validator('host_url')
def validate_uptimekuma_url(cls, v):
    # Add specific validation for UptimeKuma URL requirements
    if not str(v).strip('/'):  # Example validation
        raise ValueError("UptimeKuma URL cannot be empty after stripping slashes")
    return v
✅ Verification successful

The change to pydantic.AnyHttpUrl is safe and beneficial

The verification shows that UptimeKumaApi is used with self.authentication_config.host_url directly, and pydantic.AnyHttpUrl automatically converts to a string when used in string operations. This type change:

  • Improves input validation for URLs
  • Maintains compatibility with the uptime_kuma_api package
  • Aligns with similar patterns used in other providers (e.g., Centreon provider)

No additional validation is needed as pydantic.AnyHttpUrl provides sufficient URL validation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for UptimeKumaApi usage patterns to verify URL handling
ast-grep --pattern 'UptimeKumaApi($url)'

Length of output: 42


Script:

#!/bin/bash
# Let's search for UptimeKumaApi class usage with more context
rg -l "UptimeKumaApi" 

# Then look for the actual implementation and usage
rg "UptimeKumaApi" -A 5 -B 5

Length of output: 3102


Script:

#!/bin/bash
# Let's check the uptime_kuma_api package usage to understand URL handling
rg -p "from uptime_kuma_api import" -A 10 -B 10

# Also check if there are any existing URL validations
rg -p "host_url" -A 5 -B 5

Length of output: 6266

keep/providers/elastic_provider/elastic_provider.py (4)

7-7: LGTM: Import changes align with type safety improvements.

The addition of typing import is appropriate for the new optional type annotations.


38-41: LGTM: Improved type safety for cloud_id.

The use of typing.Optional[str] with a None default is a good practice for optional fields.


45-47: LGTM: Clear validation logic.

The validator properly handles the mutual exclusivity of host and cloud_id fields with a descriptive error message.


72-74: LGTM: Clean client initialization.

The code cleanly accesses the typed fields from the authentication config.

keep/providers/grafana_oncall_provider/grafana_oncall_provider.py (2)

4-4: LGTM: Import placement is correct.

The pydantic import is appropriately placed with other third-party imports and is necessary for the type validation changes.


31-37: Type change looks good, but let's verify backward compatibility.

The change from str to pydantic.AnyHttpUrl enhances type safety and validation. However, we should verify that this change doesn't break existing code that might be accessing the host field directly.

Let's check for direct string operations on the host field:

Note: The validation metadata key might be redundant since AnyHttpUrl already implies URL validation. However, it's not harmful and might be useful for frontend form generation.

✅ Verification successful

Type change from str to pydantic.AnyHttpUrl is safe to proceed

After analyzing the codebase usage of host field across providers, the change is safe because:

  1. All providers that use string operations on the host field (like startswith(), concatenation) already handle both string and URL types by converting the host to string format when needed.
  2. The AppDynamics provider specifically handles URL validation by checking for http:// and https:// prefixes and adding them if missing, which aligns well with AnyHttpUrl type.
  3. The pydantic.AnyHttpUrl type provides string-compatible operations through its __str__ method, ensuring backward compatibility.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for direct string operations on the host field that might break with AnyHttpUrl

# Look for string operations on host field
rg -l 'authentication_config\.host\.' keep/providers/

# Look for direct string concatenation with host
rg -l 'authentication_config\.host \+' keep/providers/

# Look for string format operations with host
rg -l 'authentication_config\.host %' keep/providers/
rg -l 'authentication_config\.host\.format' keep/providers/

Length of output: 496


Script:

#!/bin/bash
# Let's check the actual usage in the files that showed string concatenation
rg -A 2 'authentication_config\.host \+' keep/providers/redmine_provider/redmine_provider.py keep/providers/jiraonprem_provider/jiraonprem_provider.py keep/providers/jira_provider/jira_provider.py keep/providers/gitlab_provider/gitlab_provider.py keep/providers/appdynamics_provider/appdynamics_provider.py

# Also check if there are any direct assignments or comparisons with host
rg -A 2 'authentication_config\.host\s*=' keep/providers/
rg -A 2 'authentication_config\.host\s*==' keep/providers/

# Let's also check if the host is used in URL construction
rg -A 2 'urljoin.*authentication_config\.host' keep/providers/

Length of output: 797


Script:

#!/bin/bash
# Let's check the actual implementation of the AppDynamics provider to understand the host usage better
cat keep/providers/appdynamics_provider/appdynamics_provider.py

# Also let's check if there are any other string manipulations with host that our previous patterns might have missed
rg -A 2 'authentication_config\.host' keep/providers/

Length of output: 29531

keep/providers/smtp_provider/smtp_provider.py (2)

16-16: LGTM: Import of validation fields

The addition of custom validation field types aligns with the PR's objective to enhance input validation.


21-27: Verify compatibility with existing SMTP server configurations

The change from str to NoSchemeUrl enhances type safety but could affect existing configurations.

Let's verify the impact:

✅ Verification successful

The previous results show only the field definition and usage within the SMTP provider itself. Let's gather more context about test cases and example configurations to better understand the impact of this change.


The previous results show an SMTP test endpoint but we need to understand the validation and type conversion logic to assess the compatibility impact. Let's check how NoSchemeUrl is implemented and used in tests.


The previous results show that NoSchemeUrl is widely used across different providers for host fields, but we need to understand its implementation to assess backward compatibility. Let's check how it validates URLs and if it's lenient enough to handle existing SMTP server configurations.


Based on the gathered information, I can now provide the final response:

The change to NoSchemeUrl type is backward compatible

The NoSchemeUrl type is specifically designed to handle URLs without schemes, making it compatible with existing SMTP server configurations. Evidence from the codebase shows:

  • NoSchemeUrl is a common pattern used across multiple providers (SSH, Splunk, Postgres, Clickhouse)
  • The implementation inherits from AnyUrl but overrides it to explicitly allow URLs without schemes
  • No existing test cases or configurations in the codebase use scheme-prefixed SMTP URLs
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for SMTP server configurations in test files and examples
rg -g '!*.pyc' -g '!*.git*' "smtp_server.*=" -A 2

# Look for potential SMTP server patterns that might break
rg -g '!*.pyc' -g '!*.git*' "smtp://|smtps://" 

Length of output: 934


Script:

#!/bin/bash
# Let's check test files for SMTP server configurations and test cases
rg -g '*.py' -g '!*.pyc' "smtp.*test" -B 2 -A 5

# Look for any example configurations or documentation
fd -e md -e yaml -e yml -e ini -e conf | xargs rg -l "smtp.*server"

# Check for any default configurations
rg -g '*.py' "default.*smtp" -A 2

Length of output: 650


Script:

#!/bin/bash
# Let's check the SMTPSettings model and its usage
ast-grep --pattern 'class SMTPSettings'

# Look for any validation logic or type conversion
rg -g '*.py' "NoSchemeUrl" -B 2 -A 2

# Check for any configuration examples in tests
rg -g '*test*.py' "smtp_server" -B 2 -A 2

Length of output: 4501


Script:

#!/bin/bash
# Let's check the NoSchemeUrl implementation details
rg -g 'fields.py' -A 10 "class NoSchemeUrl"

# Check if there are any example configurations in documentation
fd README.md | xargs rg "smtp"

# Look for any environment variable usage patterns
rg "SMTP_SERVER.*=" -A 2 -B 2

Length of output: 842

keep/providers/redmine_provider/redmine_provider.py (2)

109-110: ⚠️ Potential issue

Security: Avoid disabling SSL verification

The code disables SSL verification (verify=False) when testing HTTPS connectivity. This is unsafe as it makes the application vulnerable to man-in-the-middle attacks.

Consider:

  1. Always verifying SSL certificates
  2. Providing a way to configure custom CA certificates
  3. Adding a configuration option for users to explicitly allow insecure connections (not recommended)
             requests.get(
                 f"https://{self.authentication_config.host}",
-                verify=False,
+                verify=True,
             )

Likely invalid or redundant comment.


20-27: 🛠️ Refactor suggestion

Consider aligning URL validation with actual usage

While the switch to pydantic.AnyHttpUrl improves type safety, there's an inconsistency between the validation and the actual code behavior. The hint suggests http://localhost:8080 (with protocol), but the code in __redmine_url handles URLs without protocols. Consider using a custom Pydantic type that matches your URL handling logic.

Here's a suggested implementation:

from pydantic import AnyHttpUrl, validator
from typing import Union

class RedmineUrl(AnyHttpUrl):
    @classmethod
    def validate(cls, value: Union[str, AnyHttpUrl]) -> AnyHttpUrl:
        if not value.startswith(('http://', 'https://')):
            value = f"https://{value}"
        return super().validate(value)
keep/providers/gitlab_provider/gitlab_provider.py (1)

21-28: Verify consistency with other provider implementations

Let's ensure this implementation aligns with the validation patterns used in other providers.

✅ Verification successful

Let me try another search pattern since the previous one had incorrect path patterns.


Based on the search results, I can now provide a final response as the verification is complete.

URL validation implementation is consistent with other providers

The implementation of host field in the GitLab provider using pydantic.AnyHttpUrl with the validation metadata is consistent with other providers in the codebase. Similar patterns are found in:

  • Grafana provider
  • Redmine provider
  • Jira OnPrem provider
  • AppDynamics provider
    and many others.

The metadata structure including required, description, sensitive, hint, and validation fields follows the established pattern across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check URL validation patterns across other providers

# Find all provider auth configs using Pydantic URL types
echo "Searching for URL validation patterns in other providers..."
rg -l "pydantic\.(AnyHttpUrl|HttpUrl|PostgresDsn|RedisDsn)" "keep/providers/**/[a-z]*_provider.py"

# Find validation hints for URL fields
echo "Searching for URL validation hints..."
ast-grep --pattern 'host: $_ = dataclasses.field(
  metadata={
    $$$
  }
)'

Length of output: 471


Script:

#!/bin/bash
# Search for all provider files first
echo "Finding provider files..."
fd "provider.py$" keep/providers

# Then search for URL validation patterns
echo -e "\nSearching for URL validation patterns..."
rg "pydantic\.(AnyHttpUrl|HttpUrl|PostgresDsn|RedisDsn)" -A 5

# Search for host field definitions
echo -e "\nSearching for host field definitions..."
rg "host.*=.*dataclasses\.field" -A 5

Length of output: 27747

keep/providers/mongodb_provider/mongodb_provider.py (2)

13-13: LGTM: Import addition for exception handling

The addition of ProviderConfigException import aligns with the enhanced validation requirements.


Line range hint 1-148: Verify secure handling of sensitive data

Let's ensure all sensitive fields are properly marked and handled securely.

✅ Verification successful

Let me verify the secure handling of sensitive data in the MongoDB provider. I'll check for consistent handling of sensitive fields and potential password logging issues.


Let me try a different approach to verify the security handling in the MongoDB provider.


Secure handling of sensitive data verified

The MongoDB provider properly handles sensitive data:

  • Password field is correctly marked with sensitive: True metadata
  • No sensitive data is logged (only debug logs for additional options parsing)
  • Credentials are safely handled in client configuration:
    • Credentials are passed directly to MongoClient without logging
    • Proper cleanup in dispose() method
    • No exposure of sensitive data in error messages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for sensitive field handling across the codebase

# Look for other sensitive field declarations to ensure consistent handling
rg -A 2 'sensitive.*True' 

# Check for potential password logging
rg -i 'password.*log'

# Verify all authentication fields are properly marked
ast-grep --pattern 'dataclasses.field.*metadata.*sensitive'

Length of output: 163569


Script:

#!/bin/bash
# Check for sensitive field handling in MongoDB provider
rg -A 2 'password.*log' keep/providers/mongodb_provider/mongodb_provider.py

# Check for proper sensitive field marking in MongoDB provider
ast-grep --pattern 'dataclasses.field.*metadata.*sensitive.*True' keep/providers/mongodb_provider/mongodb_provider.py

# Check for any password/credential logging in MongoDB provider
rg -i 'password.*debug|password.*info|password.*warning|password.*error' keep/providers/mongodb_provider/mongodb_provider.py

# Check for proper handling of credentials in client generation
ast-grep --pattern 'def __generate_client(self):
  $$$
  client_conf = {
    $$$
  }
  $$$' keep/providers/mongodb_provider/mongodb_provider.py

Length of output: 470


Script:

#!/bin/bash
# Check the full MongoDB provider implementation for sensitive data handling
cat keep/providers/mongodb_provider/mongodb_provider.py

# Check for any logging statements in MongoDB provider
rg "self\.logger\." keep/providers/mongodb_provider/mongodb_provider.py

# Look for credential handling in client configuration
rg "client_conf" -A 5 keep/providers/mongodb_provider/mongodb_provider.py

Length of output: 7333

keep/providers/ntfy_provider/ntfy_provider.py (1)

33-41: Verify URL string operations compatibility.

The __send_alert method performs string operations on the host URL. Let's verify that pydantic.AnyHttpUrl maintains string compatibility with these operations.

✅ Verification successful

Let me search for the specific usage of host URL in the ntfy provider to verify the string operations.


String operations on pydantic.AnyHttpUrl are safe in this implementation

The code performs simple string operations (endswith, string concatenation) on the host URL which are compatible with pydantic.AnyHttpUrl as it can be coerced to a string. The operations are also safely wrapped in a null check. The urljoin function from urllib is used correctly for URL composition.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all string operations on the host URL
rg -l "AnyHttpUrl" | xargs rg "host\." -A 2

Length of output: 8475


Script:

#!/bin/bash
# Search for the __send_alert method and any host URL operations in ntfy provider
rg -A 10 "__send_alert" keep/providers/ntfy_provider/ntfy_provider.py

Length of output: 807

keep/providers/webhook_provider/webhook_provider.py (1)

Line range hint 91-106: Verify webhook validation test coverage.

The validate_scopes method tests with a simple payload, but should be expanded to cover:

  • Different URL formats
  • Authentication methods
  • Error cases
keep/providers/grafana_incident_provider/grafana_incident_provider.py (1)

6-15: LGTM! Clean import organization

The imports are well-organized and the addition of HttpsUrl aligns with the PR's type safety objectives.

keep/providers/kubernetes_provider/kubernetes_provider.py (3)

2-10: LGTM: Import changes are appropriate.

The addition of pydantic and reorganization of imports support the new type validation features.


40-44: LGTM: Improved metadata for the insecure field.

The updated description is more concise, and the addition of the "switch" type improves UI integration.


17-26: Verify compatibility with all Kubernetes API server URL formats.

While pydantic.AnyHttpUrl provides robust URL validation, please verify it supports all valid Kubernetes API server URL formats, including:

  • Local development clusters (e.g., localhost:8443)
  • Custom ports
  • Internal cluster URLs
✅ Verification successful

Let me gather more information about the URL validation implementation and test cases.


Let me check the URL validation implementation in the codebase to understand how it handles different URL formats.


Based on the gathered information, I can now provide a final response about the URL validation compatibility:

The URL validation implementation supports all required Kubernetes API server URL formats

The codebase uses pydantic.AnyHttpUrl with a frontend validation layer that:

  • Accepts both HTTP and HTTPS protocols via getBaseUrlSchema({ protocols: ["http", "https"] })
  • Supports custom ports (as seen in URL validation tests)
  • Handles localhost URLs (validated in e2e tests with "http://localhost")
  • Validates internal and external URLs without TLD requirements

The implementation is consistent with other infrastructure providers (OpenShift, Cilium) that also use the same URL validation approach for API server endpoints.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for existing API server URL patterns in tests and examples

# Search for API server URL patterns in test files
echo "Searching for API server URL patterns in tests..."
rg -A 1 'api_server.*=' tests/

# Search for URL patterns in example configurations
echo "Searching for example configurations..."
rg -A 1 'KUBERNETES_URL' .

# Search for URL-related test cases
echo "Searching for URL-related test cases..."
rg -A 3 'test.*url' tests/

Length of output: 2341


Script:

#!/bin/bash
# Check URL validation implementation and test cases

# Search for URL validation related code
echo "Searching for URL validation implementation..."
rg -A 5 'any_http_url' .

# Search for kubernetes provider test files
echo "Searching for kubernetes provider tests..."
fd "test.*kubernetes.*\.py" tests/

# If test files exist, show their content
echo "Content of kubernetes provider test files..."
fd "test.*kubernetes.*\.py" tests/ -x cat {}

# Check for URL validation in the provider implementation
echo "Checking provider implementation..."
rg -A 5 "class KubernetesProvider" .

Length of output: 13096


Script:

#!/bin/bash
# Check URL validation implementation details

# Search for URL validation implementation
echo "Searching for URL validation implementation..."
rg -A 10 'getBaseUrlSchema' .

# Search for any URL-related validation tests
echo "Searching for URL validation tests..."
rg -A 10 'test.*url.*validation' .

# Check for any URL-related utilities
echo "Checking for URL-related utilities..."
fd -e ts -e py -x rg -l 'url.*valid|valid.*url' {}

Length of output: 10162

keep/providers/squadcast_provider/squadcast_provider.py (2)

16-16: LGTM: Import for enhanced type safety

The addition of HttpsUrl import aligns with the PR's objective to improve input validation and type safety.


38-46: Verify backward compatibility of webhook_url type change

The change from str to HttpsUrl enhances type safety but could potentially break existing configurations. Please ensure that:

  1. Existing webhook URLs in production are HTTPS compliant
  2. Migration documentation is updated if needed
✅ Verification successful

Type change for webhook_url is safe and properly validated

The test file demonstrates that the code already validates and rejects non-HTTPS URLs:

  • HTTP URLs (both localhost and example.com) trigger error messages
  • Only HTTPS URLs are accepted

This indicates the type change from str to HttpsUrl is formalizing existing behavior rather than introducing breaking changes. No migration documentation is needed since HTTPS was already required.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for webhook_url usage patterns in tests and configurations
rg -l 'webhook_url.*http:' 
rg -l 'webhook_url.*localhost'

Length of output: 131


Script:

#!/bin/bash
# Check the actual webhook_url usage in the test file
rg -A 5 'webhook_url.*http:|webhook_url.*localhost' tests/e2e_tests/test_end_to_end.py

Length of output: 511

keep/providers/slack_provider/slack_provider.py (3)

16-16: LGTM: Import of HttpsUrl validation field

The addition of the HttpsUrl import aligns with the PR's objective to enhance input validation for provider configurations.


23-29: Verify the impact of removing the default value

The removal of the default value for webhook_url could potentially break existing code that relies on it. Let's verify the usage across the codebase.

✅ Verification successful

Based on the results, I can now generate the final response:

Removing the default value for webhook_url is safe

The search results show that SlackProviderAuthConfig is only instantiated once in the codebase (in slack_provider.py) and it's created using the **self.config.authentication pattern, which means the webhook_url is provided through configuration. There are no instances of direct instantiation that might rely on default values.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for instances where SlackProviderAuthConfig is instantiated without webhook_url
ast-grep --pattern 'SlackProviderAuthConfig($$$)' 

# Search for direct references to webhook_url
rg -A 2 'webhook_url\s*=' --type py

Length of output: 4349


23-29: Verify error handling with the new HttpsUrl type

The change to HttpsUrl type affects multiple integration points. Please ensure:

  1. The validate_config method properly handles HttpsUrl validation errors
  2. The _notify method correctly handles the HttpsUrl type when making requests
  3. Error messages are clear when invalid URLs are provided
✅ Verification successful

Let me gather more information about the validation and error handling in the slack provider.


HttpsUrl validation and error handling are properly implemented

The verification shows that the code handles the HttpsUrl type appropriately:

  1. The validate_config method checks for either webhook_url or access_token presence
  2. The _notify method properly handles the webhook_url:
    • Uses requests.post with the validated URL
    • Raises ProviderException with clear error message on request failure
  3. The HttpsUrl type is properly imported and used with dataclass field validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check error handling patterns in the codebase
rg -A 5 'raise (ProviderException|Exception).*url' --type py

# Look for similar HttpsUrl validation patterns in other providers
ast-grep --pattern 'class $name:
  $$$
  webhook_url: HttpsUrl = $$$
  $$$
'

Length of output: 2007


Script:

#!/bin/bash
# Check the validation and error handling in slack provider
rg -A 10 'def validate_config|def _notify' keep/providers/slack_provider/slack_provider.py

# Check for HttpsUrl usage and validation
rg -A 5 'HttpsUrl' keep/providers/slack_provider/slack_provider.py

# Look for request handling with webhook_url
rg -A 5 'webhook_url' keep/providers/slack_provider/slack_provider.py

Length of output: 2949

keep/providers/victoriametrics_provider/victoriametrics_provider.py (2)

10-10: LGTM: Import changes align with type safety objectives.

The addition of AnyHttpUrl and UrlPort imports supports the enhanced type validation requirements.

Also applies to: 16-16


30-35: LGTM: Enhanced type safety for configuration fields.

The changes properly implement stronger type validation for URLs and ports:

  • VMAlertHost: Now validates HTTP/HTTPS URLs
  • VMAlertPort: Uses custom UrlPort type for port validation
  • VMAlertURL: Ensures valid URL format

The metadata updates provide clear examples and validation hints.

Also applies to: 42-47, 54-61

keep/providers/kafka_provider/kafka_provider.py (2)

25-32: 🛠️ Refactor suggestion

Add comprehensive connection validation during config initialization

The current implementation validates the URL format but doesn't validate the actual connection details until runtime in validate_scopes(). This could lead to configurations passing initial validation but failing during operation.

Consider moving the connection validation logic to the validate_config() method:

def validate_config(self):
    """
    Validates required configuration for Kafka provider.
    """
    self.authentication_config = KafkaProviderAuthConfig(
        **self.config.authentication
    )
    
    # Validate connection configuration early
    conf = self._get_conf()
    try:
        # Test connection with initial security protocol
        consumer = KafkaConsumer(
            group_id="keephq-validation",
            **{k: v for k, v in conf.items() if k != 'group_id'}
        )
        consumer.close()
    except NoBrokersAvailable:
        # Retry with SASL_PLAINTEXT
        try:
            conf["security_protocol"] = "SASL_PLAINTEXT"
            consumer = KafkaConsumer(
                group_id="keephq-validation",
                **{k: v for k, v in conf.items() if k != 'group_id'}
            )
            consumer.close()
        except NoBrokersAvailable as e:
            raise ValueError(f"Could not connect to Kafka at {self.authentication_config.host}: {str(e)}")
    except KafkaError as e:
        raise ValueError(f"Invalid Kafka configuration: {str(e)}")

This ensures configuration issues are caught early during provider initialization rather than during operation.

Let's check for similar validation patterns in other providers:

Also applies to: 156-177


Line range hint 156-177: Enhance security protocol handling and sensitive data logging

The current implementation has several security considerations that should be addressed:

  1. The automatic fallback to SASL_PLAINTEXT could expose credentials over an unencrypted connection
  2. Connection errors might log sensitive information
  3. The security protocol selection logic is duplicated

Consider the following improvements:

  1. Add explicit security protocol configuration:
@pydantic.dataclasses.dataclass
class KafkaProviderAuthConfig:
    # ... existing fields ...
    security_protocol: str = dataclasses.field(
        default="SASL_SSL",
        metadata={
            "required": False,
            "description": "Security protocol",
            "hint": "SASL_SSL, SASL_PLAINTEXT, PLAINTEXT",
            "validation": "enum",
            "enum_values": ["SASL_SSL", "SASL_PLAINTEXT", "PLAINTEXT"]
        }
    )
  1. Remove automatic fallback to PLAINTEXT:
def _get_conf(self):
    if self.authentication_config.security_protocol == "PLAINTEXT":
        self.logger.warning("Using unsecured PLAINTEXT protocol")
        
    basic_conf = {
        "bootstrap_servers": self.authentication_config.host,
        "security_protocol": self.authentication_config.security_protocol,
        # ... rest of the configuration
    }
  1. Ensure sensitive data is not logged:
def validate_scopes(self):
    try:
        consumer = KafkaConsumer(self.authentication_config.topic, **conf)
    except NoBrokersAvailable as e:
        self.err = "Could not connect to Kafka broker"
        self.logger.warning(self.err, extra={
            "error": str(e),
            "host": self.authentication_config.host.split("@")[-1]  # Remove credentials from URL if present
        })

Let's check for sensitive data logging patterns:

Also applies to: 192-207

tests/e2e_tests/test_end_to_end.py (1)

39-39: LGTM: Import statement correctly added

The expect import from playwright.sync_api is appropriately placed and necessary for the validation assertions in the new test.

keep/providers/appdynamics_provider/appdynamics_provider.py (2)

61-66: LGTM! Enhanced URL validation using Pydantic.

The change from str to pydantic.AnyHttpUrl improves type safety and provides built-in URL validation. The hint example and validation metadata are well documented.


61-66: Verify the impact on existing AppDynamics instances.

Since we're enforcing stricter URL validation, let's verify that this change won't break existing AppDynamics instances that might have non-standard URLs.

✅ Verification successful

URL validation change appears safe for AppDynamics instances

Based on the codebase analysis:

  • The only AppDynamics URLs found follow the standard format https://{account}.saas.appdynamics.com
  • The example URL in the code matches this pattern: https://baseball202404101029219.saas.appdynamics.com
  • No non-standard or legacy URL formats were found in tests, documentation, or configuration files
  • The pydantic.AnyHttpUrl validation will accept any valid HTTPS URL, which is compatible with AppDynamics' URL format
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for AppDynamics host configurations in test files and documentation
# to ensure compatibility with the new validation

echo "Searching for AppDynamics host examples in tests..."
rg -g '!*.pyc' -g '!*.git*' "appdynamics.*host.*=" 

echo "Searching for AppDynamics URLs in documentation..."
rg -g '*.md' -g '*.rst' "appdynamics.*\.saas\.appdynamics\.com"

Length of output: 342


Script:

#!/bin/bash
# Let's try a broader search for AppDynamics configurations and tests

# Search for any AppDynamics host/URL patterns in the codebase
echo "Searching for AppDynamics URL patterns..."
rg -g '!*.pyc' -g '!*.git*' -i "https?://[^\"'\s]*appdynamics\.com"

# Search for test files related to AppDynamics
echo -e "\nSearching for AppDynamics test files..."
fd -t f "test.*appdynamics.*\.(py|yaml|json)$"

# Search for AppDynamics configuration examples
echo -e "\nSearching for AppDynamics configuration examples..."
rg -g '!*.pyc' -g '!*.git*' "host.*appdynamics" -A 2 -B 2

Length of output: 2340


Script:

#!/bin/bash
# Let's check for any existing AppDynamics configurations in test data or fixtures

# Search for test data files
echo "Searching for AppDynamics test data..."
fd -t f "(test|fixture).*\.(yaml|json|toml)$" -x rg -l "appdynamics"

# Search for any URL validation related tests
echo -e "\nSearching for URL validation tests..."
rg -g "test_*.py" "AnyHttpUrl|URLValidator|validate.*url" -A 3 -B 3

# Check for any existing AppDynamics configurations
echo -e "\nSearching for AppDynamics configuration examples in tests..."
rg -g "test_*.py" "appdynamics.*provider" -A 5 -B 5

Length of output: 507

keep/providers/splunk_provider/splunk_provider.py (2)

16-16: LGTM: Import of custom validation types

The addition of custom validation types aligns with the PR's objective of enhancing input validation for provider configurations.


29-35: LGTM: Enhanced type safety for host and port fields

The migration from basic types (str, int) to custom validated types (NoSchemeUrl, UrlPort) improves input validation and type safety. The metadata and defaults are preserved, maintaining backward compatibility.

Also applies to: 36-42

keep/providers/ilert_provider/ilert_provider.py (1)

19-19: LGTM: Import of HttpsUrl validation field

The addition of the HttpsUrl import aligns with the PR's objective to enhance type safety and input validation.

keep/providers/servicenow_provider/servicenow_provider.py (2)

17-17: LGTM: Import for enhanced type validation

The addition of HttpsUrl import aligns with the PR's objective of improving type safety for provider inputs.


24-31: LGTM: Enhanced URL validation with HttpsUrl type

The transition from str to HttpsUrl type enhances input validation and type safety. The metadata provides clear guidance with an appropriate example.

keep/providers/jira_provider/jira_provider.py (1)

17-17: LGTM: Type safety enhancement for host field

The change from str to HttpsUrl type with additional metadata improves type safety and provides better user guidance through the hint field.

Also applies to: 41-49

keep/providers/sentry_provider/sentry_provider.py (2)

19-19: LGTM! Import aligns with validation enhancement goals

The addition of HttpsUrl import supports the transition to stronger type validation for URL fields.


37-45: LGTM! Enhanced type safety for api_url field

The transition to HttpsUrl type enforces HTTPS-only URLs, improving security. The default value is appropriate and consistent with the SENTRY_DEFAULT_API constant.

Let's verify that no HTTP URLs are currently in use:

✅ Verification successful

No HTTP Sentry URLs found in the codebase, type safety enhancement verified

The verification confirms that there are no non-HTTPS Sentry API URLs in use throughout the codebase, making the HttpsUrl type change a safe enhancement that aligns with existing practices.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential HTTP Sentry API URLs in the codebase
rg -l "http://[^s].*sentry\.io" || echo "No HTTP URLs found"

# Check for any environment variables or configuration files that might override the API URL
rg -l "SENTRY_API_URL.*http://[^s]" || echo "No HTTP URLs in environment configurations"

Length of output: 213

keep/providers/zabbix_provider/zabbix_provider.py (1)

Line range hint 673-674: Update test configuration to use HTTPS URL.

The test configuration uses an HTTP localhost URL which would fail if we enforce HTTPS URLs. Consider updating it to use HTTPS or adding a development-only exception for localhost URLs.

keep/providers/graylog_provider/graylog_provider.py (1)

Line range hint 1-700: Implementation looks good overall!

The changes enhance input validation by leveraging Pydantic's URL validation, which aligns well with the PR objectives. The implementation maintains good error handling and logging practices throughout the codebase.

keep/providers/newrelic_provider/newrelic_provider.py (3)

19-19: LGTM: HttpsUrl import aligns with validation enhancement goals

The addition of the HttpsUrl import from keep.validation.fields supports the PR's objective of enhancing input validation.


165-165: LGTM: Improved scope validation feedback

The change from False to "Invalid" provides more descriptive feedback for invalid scopes.


Line range hint 32-38: Verify error handling for URL validation

The code should properly handle and report URL validation errors to users.

Also applies to: 41-47, 165-165

✅ Verification successful

Based on the gathered information, I can now provide a final response:

URL validation is properly handled through HttpsUrl type and field validation

The code uses the HttpsUrl type (from keep/validation/fields.py) which inherits from HttpUrl and enforces HTTPS scheme validation. The URL field in the NewRelic provider is defined with:

  • Type annotation as HttpsUrl
  • Metadata including "validation": "https_url"
  • Required field validation

This ensures proper URL validation and error reporting to users through the validation framework.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check error handling for invalid URLs across the codebase

# Test 1: Search for URL validation error handling patterns
rg -A 3 "raise (ProviderConfigException|ProviderException|ValueError).*URL"

# Test 2: Search for URL validation in tests
rg -A 5 "def test.*url.*invalid" tests/

Length of output: 470


Script:

#!/bin/bash
# Let's check how URLs are validated in the NewRelic provider and similar providers

# Check URL validation in NewRelic provider
rg -A 5 "url.*=|URL.*=" keep/providers/newrelic_provider/

# Check URL-related validation in provider base classes
rg -A 5 "(validate|check).*url" keep/providers/

# Look for URL validation patterns in the NewRelic tests
rg -A 5 "(test|check|validate).*url" tests/providers/newrelic_provider/

# Check for URL field definitions and validations
ast-grep --pattern 'url: str = dataclasses.field($$$)'

Length of output: 14249


Script:

#!/bin/bash
# Let's check for HttpsUrl type definition and validation

# Check for HttpsUrl type definition
rg -A 5 "class HttpsUrl"

# Check for URL validation in the base provider
rg -A 5 "def validate" keep/providers/provider.py

# Check for URL-related exceptions
rg -A 3 "class.*Exception" keep/providers/

# Look for URL validation in config validation
rg -A 5 "validate.*config" keep/providers/

Length of output: 56295

keep/providers/datadog_provider/datadog_provider.py (1)

72-80: LGTM! Good type safety improvement

The change from str to HttpsUrl type for the domain field enhances type safety and validation. The added validation metadata clearly specifies the HTTPS URL requirement.

keep/validation/fields.py (3)

9-14: Confirm the default port implementation in HttpsUrl

The get_default_parts method in the HttpsUrl class sets the default port to 443. This ensures that URLs without an explicitly defined port default to the standard HTTPS port, which aligns with expected behavior.


6-6: Verify the usage of UrlPort within the codebase

The UrlPort type is defined but not used within this file. Ensure that it is utilized where needed or consider removing it if it's unnecessary.

Run the following script to check for usages of UrlPort in the codebase:

✅ Verification successful

UrlPort is actively used across multiple provider modules

The type is properly utilized throughout the codebase:

  • Imported and used in 8 different provider modules for port validation
  • Used consistently for port-related fields in dataclass definitions
  • Applied for various services including Postgres, SSH, Splunk, SMTP, VictoriaMetrics, OpenObserve, Kibana, and Clickhouse
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of 'UrlPort' in the project.

# Test: Search for the 'UrlPort' type. Expect: Occurrences where it's used.
rg 'UrlPort'

Length of output: 1690


17-46: 🛠️ Refactor suggestion

Refactor scheme handling in NoSchemeUrl to avoid using placeholder schemes

Assigning "foo" as a placeholder scheme in validate_parts and then removing it in build may lead to confusion or unexpected behavior. Consider refactoring to handle URLs without a scheme more elegantly.

Apply this diff to adjust the scheme handling:

-    scheme = parts["scheme"]
-    parts["scheme"] = "foo" if scheme is None else scheme
+    if parts["scheme"] is None:
+        parts["scheme"] = ''

In the build method, modify the URL construction to handle the absence of a scheme without relying on splitting the string:

-    return url.split("://")[1]
+    if '://' in url:
+        return url.split('://', 1)[1]
+    else:
+        return url

This approach preserves the integrity of the URL and avoids introducing arbitrary scheme values.

Likely invalid or redundant comment.

keep/providers/base/base_provider.py (4)

22-23: Approved: Updated imports are appropriate

The import of get_provider_by_name and is_linked_provider is necessary for the new methods is_installed and format_alert.


28-29: Approved: Importing parse_and_enrich_deleted_and_assignees

The import is required for enrichment functions and aligns with the new code.


714-721: Approved: Addition of is_installed property

The is_installed property correctly checks if the provider has been recorded in the database and enhances the provider's configurability.


722-731: Verify the necessity of importing Parser within is_provisioned property

Importing Parser inside the is_provisioned property may be a workaround for circular import issues. Confirm that this import placement is required and won't introduce performance overhead or maintainability concerns.

If the circular dependency can be resolved, consider moving the import to the top level. Otherwise, document the reason for the local import to aid future maintainers.

keep-ui/app/providers/provider-form.tsx (1)

110-115: Avoid using spread syntax in reducers for better performance.

Similar to the previous function, using spread syntax in getOptionalConfigs can lead to performance issues. Mutate the accumulator directly to enhance efficiency.

🧰 Tools
🪛 Biome

[error] 115-115: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

Comment on lines 118 to 120
size={
window.innerWidth < 640 ? 100 : window.innerWidth < 1024 ? 80 : 40
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider hydration safety and window resize handling

The direct access to window.innerWidth in the render method could cause hydration mismatches and won't respond to window resizes. Consider using a hook-based approach for better reliability.

Here's a suggested implementation:

+import { useEffect, useState } from "react";
+
+const usePanelSize = () => {
+  const [size, setSize] = useState(40);
+
+  useEffect(() => {
+    const updateSize = () => {
+      const width = window.innerWidth;
+      setSize(width < 640 ? 100 : width < 1024 ? 80 : 40);
+    };
+
+    updateSize();
+    window.addEventListener('resize', updateSize);
+    return () => window.removeEventListener('resize', updateSize);
+  }, []);
+
+  return size;
+};

 const ProvidersTiles = ({ ... }) => {
+  const panelSize = usePanelSize();
   
   return (
     <SlidingPanel
       type={"right"}
       isOpen={openPanel}
-      size={
-        window.innerWidth < 640 ? 100 : window.innerWidth < 1024 ? 80 : 40
-      }
+      size={panelSize}
       backdropClicked={handleCloseModal}
📝 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
size={
window.innerWidth < 640 ? 100 : window.innerWidth < 1024 ? 80 : 40
}
import { useEffect, useState } from "react";
const usePanelSize = () => {
const [size, setSize] = useState(40);
useEffect(() => {
const updateSize = () => {
const width = window.innerWidth;
setSize(width < 640 ? 100 : width < 1024 ? 80 : 40);
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
};
const ProvidersTiles = ({ ... }) => {
const panelSize = usePanelSize();
return (
<SlidingPanel
type={"right"}
isOpen={openPanel}
size={panelSize}
backdropClicked={handleCloseModal}

Comment on lines +24 to +29
host: pydantic.AnyUrl = dataclasses.field(
metadata={
"required": True,
"description": "MySQL hostname",
"validation": "any_url",
}
Copy link

@coderabbitai coderabbitai bot Nov 19, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider using a more appropriate type for MySQL host validation

The change from str to pydantic.AnyUrl might be too restrictive for MySQL host configurations. MySQL connections commonly support various host formats including:

  • Hostnames (e.g., "db.example.com")
  • IP addresses (e.g., "192.168.1.1")
  • Unix domain sockets (e.g., "/var/run/mysqld/mysqld.sock")
  • Special values (e.g., "localhost")

The AnyUrl type will reject valid MySQL host values that don't conform to URL syntax.

Consider these alternatives:

-    host: pydantic.AnyUrl = dataclasses.field(
+    host: str = dataclasses.field(
         metadata={
             "required": True,
             "description": "MySQL hostname",
-            "validation": "any_url",
+            "pattern": "^[a-zA-Z0-9-._]+$|^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$|^/[a-zA-Z0-9-._/]+$",
         }
     )

Or create a custom type:

class MySQLHost(str):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        if not isinstance(v, str):
            raise TypeError('string required')
        # Add validation for hostname, IP, socket path
        return cls(v)

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines 59 to 62
if self.is_installed or self.is_provisioned:
host = self.config.authentication["domain"]
host = "https://" + host if not host.startswith("https://") else host
self.config.authentication["domain"] = host
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider removing redundant HTTPS scheme handling.

The manual HTTPS scheme prepending seems redundant since HttpsUrl type should already handle this validation. This could lead to double transformation of the URL.

Consider simplifying to:

-            host = self.config.authentication["domain"]
-            host = "https://" + host if not host.startswith("https://") else host
-            self.config.authentication["domain"] = host

Let the HttpsUrl type handle the validation and transformation.

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

Comment on lines 23 to 38
domain: HttpsUrl = dataclasses.field(
metadata={
"required": True,
"description": "Auth0 API Token",
"hint": "https://manage.auth0.com/dashboard/us/YOUR_ACCOUNT/apis/management/explorer",
"description": "Auth0 Domain",
"hint": "https://tenantname.us.auth0.com",
"validation": "https_url",
},
)
domain: str = dataclasses.field(

token: str = dataclasses.field(
default=None,
metadata={
"required": True,
"description": "Auth0 Domain",
"hint": "tenantname.us.auth0.com",
"description": "Auth0 API Token",
"hint": "https://manage.auth0.com/dashboard/us/YOUR_ACCOUNT/apis/management/explorer",
},
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the gathered information, I can now provide the final response:

Auth0 provider validation needs test coverage

The validation changes for Auth0 provider configuration lack test coverage:

  • No unit tests for Auth0 provider validation
  • No end-to-end tests for Auth0 provider field validation
  • Other providers (Kibana, Teams, MongoDB, etc.) have validation tests, but Auth0 is missing

Please add:

  • Unit tests for Auth0 provider field validation
  • End-to-end test case in test_provider_validation to verify:
    • Domain URL format validation
    • Required token validation
    • Error message display for invalid inputs
🔗 Analysis chain

Verify Auth0 provider configuration in end-to-end tests.

The validation changes look good, but let's ensure they're covered by tests.


Let me gather more information about Auth0 provider tests.


Let me check the content of the identified files to understand the test coverage.


Let me check the end-to-end test implementation for provider validation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for Auth0 provider tests
rg -l "Auth0Provider.*test" 

# Check for validation testing patterns
rg "domain.*invalid|incorrect" -A 5 -g "*test*.py"

Length of output: 3217


Script:

#!/bin/bash
# Look for Auth0 provider test files
fd "test.*auth.*py|auth.*test.*py"

# Check for Auth0 provider validation tests in the entire codebase
rg -l "Auth0Provider" tests/
rg "def test.*auth0" tests/

# Look for any Auth0-related test configuration
rg -l "auth0.*domain|auth0.*token" tests/

Length of output: 183


Script:

#!/bin/bash
# Check Auth0 provider test content
cat tests/test_auth.py

# Check Auth0-related test fixtures
cat tests/fixtures/client.py

# Look for any provider validation tests
rg "provider.*validation|validate.*provider" tests/ -A 5

Length of output: 14946


Script:

#!/bin/bash
# Check the e2e test implementation for provider validation
cat tests/e2e_tests/test_end_to_end.py

# Look for any Auth0-specific provider tests in the e2e tests
rg "auth0" tests/e2e_tests/ -A 5

Length of output: 10783

Comment on lines 174 to 176
results = clickhouse_provider.query(
query="SELECT * FROM logs_table ORDER BY timestamp DESC LIMIT 1"
)
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 missing required port in test configuration

The test configuration in the main block is missing the required port field, which is now mandatory with the new UrlPort type.

Apply this diff to fix the configuration:

     config = ProviderConfig(
         authentication={
             "username": os.environ.get("CLICKHOUSE_USER"),
             "password": os.environ.get("CLICKHOUSE_PASSWORD"),
             "host": os.environ.get("CLICKHOUSE_HOST"),
             "database": os.environ.get("CLICKHOUSE_DATABASE"),
+            "port": os.environ.get("CLICKHOUSE_PORT"),
         }
     )

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

Comment on lines 60 to 61
if cls.user_required and user is None:
raise errors.UrlUserInfoError()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define user_required in NoSchemeUrl to prevent AttributeError

The validate_parts method checks for cls.user_required, but user_required is not defined in the NoSchemeUrl class. This could result in an AttributeError.

Add the user_required attribute to the NoSchemeUrl class:

 class NoSchemeUrl(AnyUrl):
     """Override to allow url without a scheme."""
 
+    user_required = False

Alternatively, if user information is not needed for URLs without a scheme, you may remove the user validation check altogether.

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

Comment on lines 151 to 152
raise ProviderException(f"Error getting host status from Centreon: {e}")

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Raise exceptions with from e to maintain exception context

When re-raising exceptions, using raise ... from e preserves the original exception's traceback, which is helpful for debugging. It provides a clear chain of exceptions, showing how the error was propagated.

Apply this diff to modify your exception handling:

 raise ProviderException(f"Error getting host status from Centreon: {e}")
+      from e
 raise ProviderException(f"Error getting service status from Centreon: {e}")
+      from e

Also applies to: 184-185

🧰 Tools
🪛 Ruff

151-151: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

Comment on lines 178 to 179
const [formValues, setFormValues] = useState<ProviderFormData>(() =>
getInitialFormValues(provider)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for undefined 'accessToken' to prevent potential runtime errors.

The accessToken obtained from session?.accessToken could be undefined if the user is not authenticated. Ensure that you verify its existence before using it in API calls to prevent runtime exceptions.

Refactor the code to handle undefined accessToken:

 const { data: session } = useSession();
+const accessToken = session?.accessToken;
+
+if (!accessToken) {
+  toast.error("Please log in to continue.");
+  return;
+}

 const callInstallWebhook = async () => {
+  if (!accessToken) {
+    toast.error("You need to be logged in to install the webhook.");
+    return;
+  }
   await installWebhook(provider, accessToken, apiUrl);
 };

 async function handleConnectClick() {
+  if (!accessToken) {
+    toast.error("Authentication required to connect the provider.");
+    onConnectChange?.(false, false);
+    return;
+  }
   if (!validate()) return;

Also applies to: 212-214, 433-434

Comment on lines 104 to 106
return Object.entries(config)
.filter(([_, config]) => config.required && !config.config_main_group)
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using spread syntax in reducers for better performance.

Using spread syntax ({ ...acc, [key]: value }) inside a reduce function leads to O(n²) time complexity due to object copying on each iteration. To improve performance, consider mutating the accumulator directly.

Apply this diff to optimize the code:

 function getRequiredConfigs(config: Provider["config"]): Provider["config"] {
-  return Object.entries(config)
-    .filter(([_, config]) => config.required && !config.config_main_group)
-    .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
+  const requiredConfigs: Provider["config"] = {};
+  Object.entries(config)
+    .filter(([_, config]) => config.required && !config.config_main_group)
+    .forEach(([key, value]) => {
+      requiredConfigs[key] = value;
+    });
+  return requiredConfigs;
 }
📝 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
return Object.entries(config)
.filter(([_, config]) => config.required && !config.config_main_group)
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
const requiredConfigs: Provider["config"] = {};
Object.entries(config)
.filter(([_, config]) => config.required && !config.config_main_group)
.forEach(([key, value]) => {
requiredConfigs[key] = value;
});
return requiredConfigs;
🧰 Tools
🪛 Biome

[error] 106-106: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

Comment on lines 208 to 227
if (config.type === "file") {
const baseSchema = z
.instanceof(File, { message: "Please upload a file here." })
.or(z.string())
.refine(
(file) => {
if (config.file_type == undefined) return true;
if (config.file_type.length <= 1) return true;
if (typeof file === "string" && installed) return true;
return (
typeof file !== "string" && config.file_type.includes(file.type)
);
},
{
message:
config.file_type && config.file_type?.split(",").length > 1
? `File type should be one of ${config.file_type}.`
: `File should be of type ${config.file_type}.`,
}
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent handling of config.file_type as both string and array

The config.file_type is inconsistently treated as an array in some places and a string in others. This may lead to errors during file type validation.

Apply this diff to consistently treat config.file_type as an array:

             .refine(
               (file) => {
                 if (config.file_type == undefined) return true;
-                if (config.file_type.length <= 1) return true;
+                if (config.file_type.length === 0) return true;
                 if (typeof file === "string" && installed) return true;
                 return (
                   typeof file !== "string" && config.file_type.includes(file.type)
                 );
               },
               {
                 message:
-                  config.file_type && config.file_type?.split(",").length > 1
-                    ? `File type should be one of ${config.file_type}.`
-                    : `File should be of type ${config.file_type}.`,
+                  config.file_type.length > 1
+                    ? `File type should be one of ${config.file_type.join(", ")}.`
+                    : `File should be of type ${config.file_type[0]}.`,
               }
             );

Ensure that config.file_type is consistently defined and used as an array throughout the code.

📝 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
if (config.type === "file") {
const baseSchema = z
.instanceof(File, { message: "Please upload a file here." })
.or(z.string())
.refine(
(file) => {
if (config.file_type == undefined) return true;
if (config.file_type.length <= 1) return true;
if (typeof file === "string" && installed) return true;
return (
typeof file !== "string" && config.file_type.includes(file.type)
);
},
{
message:
config.file_type && config.file_type?.split(",").length > 1
? `File type should be one of ${config.file_type}.`
: `File should be of type ${config.file_type}.`,
}
);
if (config.type === "file") {
const baseSchema = z
.instanceof(File, { message: "Please upload a file here." })
.or(z.string())
.refine(
(file) => {
if (config.file_type == undefined) return true;
if (config.file_type.length === 0) return true;
if (typeof file === "string" && installed) return true;
return (
typeof file !== "string" && config.file_type.includes(file.type)
);
},
{
message:
config.file_type.length > 1
? `File type should be one of ${config.file_type.join(", ")}.`
: `File should be of type ${config.file_type[0]}.`,
}
);

@Kiryous
Copy link
Contributor

Kiryous commented Nov 20, 2024

@theedigerati hey! thank you for the contribution! I'll pick it up from Matvey and review the changes.

In the meantime, could you please check the coderabbit comments? Feel free to resolve not important ones, but I also pretty important ones — I tagged you.

@Kiryous Kiryous requested review from Kiryous and removed request for Matvey-Kuk November 20, 2024 13:41
@theedigerati theedigerati reopened this Nov 20, 2024
@theedigerati
Copy link
Author

@Kiryous Alright. Thank you very much. I'll get on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request Provider Providers related issues size:XXL This PR changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[🔨 Enhancement]: better input validation for providers / add types for inputs
4 participants