diff --git a/README.md b/README.md index f44b1ed8f..281213d3d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Detection Rules contains more than just static rule files. This repository also |------------------------------------------------ |------------------------------------------------------------------------------------ | | [`detection_rules/`](detection_rules) | Python module for rule parsing, validating and packaging | | [`etc/`](detection_rules/etc) | Miscellaneous files, such as ECS and Beats schemas | +|[`hunting`](./hunting/)|Root directory where threat hunting queries are stored| | [`kibana/`](lib/kibana) | Python library for handling the API calls to Kibana and the Detection Engine | | [`kql/`](lib/kql) | Python library for parsing and validating Kibana Query Language | | [`rta/`](rta) | Red Team Automation code used to emulate attacker techniques, used for rule testing | diff --git a/hunting/README.md b/hunting/README.md new file mode 100644 index 000000000..b2eb1b564 --- /dev/null +++ b/hunting/README.md @@ -0,0 +1,60 @@ +# Hunt Queries + +--- + +Welcome to the `hunting` folder within the `detection-rules` repository! This directory houses a curated collection of threat hunting queries designed to enhance security monitoring and threat detection capabilities using the Elastic Stack. Each file in this directory provides a query tailored for identifying specific security threats or suspicious activities. + +These queries are designed for use with the Elastic Security platform, part of the broader Elastic Stack, enabling security teams to proactively hunt for potential threats in their environment. + +- KQL +- EQL +- ES|QL +- OsQuery +- YARA + +## How to Contribute + +Contributing to the `hunting` folder is a great way to share your expertise and enhance the security community's capabilities. Here’s how you can contribute: + +### Adding New Queries +- **TOML File Naming and Organization**: Ensure that any new queries are named descriptively and grouped by the type of threat they address. Place your TOML files inside the `queries` folder and ensure they are named in a way that reflects the nature of the threat or behavior they are designed to detect. +- **TOML Fields**: To ensure the hunt queries are consistent and comprehensive, it's important to structure the threat detection rules with specific fields. When contributing a new rule, please include the following fields in the TOML file to describe and configure the analytic: + - **author**: The name of the individual or organization authoring the rule. + - **integration**: The specific integration or data source the rule applies to, such as `aws_bedrock.invocation`. + - **uuid**: A unique identifier for the rule to maintain version control and tracking. + - **name**: A descriptive name for the rule that clearly indicates its purpose. + - **language**: The query language used in the rule, such as `KQL`, `EQL`, `ES|QL`, `OsQuery`, or `YARA`. + - **query**: The actual query or analytic expression written in the appropriate query language that executes the detection logic. + - **notes**: An array of strings providing detailed insights into the rationale behind the rule, suggestions for further investigation, and tips on distinguishing false positives from true activity. + - **mitre**: Reference to applicable MITRE ATT&CK tactics or techniques that the rule addresses, enhancing the contextual understanding of its security implications. + - **references**: Links to external documents, research papers, or websites that provide additional information or validation for the detection logic. + +- **Documentation (Optional)**: Include a `README.md` in each subfolder describing the queries and their purposes. This would include a brief description of the new category. + +### Field Usage +Use standardized fields where possible to ensure that queries are compatible across different data environments and sources. + +### Review and Pull Requests +Follow the standard [contributing guide](../CONTRIBUTING.md). Please remember to use the generate_markdown.py script to update the documentation after adding or updateing queries. + +## Using the Script to Generate Markdown + +The `generate_markdown.py` script is provided to automate the creation of Markdown files from TOML rule definitions. Here’s how to use it: + +- **Generating Markdown**: Run `python generate_markdown.py` from the root of the `hunting` directory. This will generate Markdown files for each TOML file and update the `index.md` to include links to the new Markdown files. +- **Structure**: Rules should be written in TOML and saved under the respective `hunt/*/rules/` directory. The script will automatically convert them into Markdown and save them in the `docs` directory within the respective category folder. + +### Sample Directory Structure Example + +```config +. +├── README.md +├── generate_markdown.py +├── index.md +└── categorical_folder_name + ├── README.md + ├── docs + │ └── generated_markdown.md + └── rules + └── hunt_query.toml +``` diff --git a/hunting/__init__.py b/hunting/__init__.py new file mode 100644 index 000000000..72ea1f6e2 --- /dev/null +++ b/hunting/__init__.py @@ -0,0 +1,4 @@ +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. diff --git a/hunting/generate_markdown.py b/hunting/generate_markdown.py new file mode 100644 index 000000000..a1da0afc8 --- /dev/null +++ b/hunting/generate_markdown.py @@ -0,0 +1,80 @@ +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. + +"""Lightweight builtin toml-markdown converter.""" + +import tomllib +from dataclasses import dataclass, field +from pathlib import Path +from typing import List, Optional + +HUNTING_DIR = Path(__file__).parent + + +@dataclass +class Hunt: + """Dataclass to represent a hunt.""" + + author: str + integration: str + uuid: str + name: str + language: str + query: str + notes: Optional[List[str]] = field(default_factory=list) + mitre: Optional[List[str]] = field(default_factory=list) + references: Optional[List[str]] = field(default_factory=list) + + +def load_toml(contents: str) -> Hunt: + """Load and validate TOML content as Hunt dataclass.""" + toml_dict = tomllib.loads(contents) + return Hunt(**toml_dict["hunt"]) + + +def load_all_toml(base_path: Path) -> List[tuple[Hunt, Path]]: + """Load all TOML files from the directory and return a list of Hunt configurations and their paths.""" + hunts = [] + for toml_file in base_path.rglob("*.toml"): + hunt_config = load_toml(toml_file.read_text()) + hunts.append((hunt_config, toml_file)) + return hunts + + +def convert_toml_to_markdown(hunt_config: Hunt, file_path: Path) -> str: + """Convert Hunt to Markdown format.""" + markdown = f"# {hunt_config.name}\n\n---\n\n" + markdown += "## Metadata\n\n" + markdown += f"- **Author:** {hunt_config.author}\n" + markdown += f"- **UUID:** `{hunt_config.uuid}`\n" + markdown += f"- **Integration:** `{hunt_config.integration}`\n" + markdown += f"- **Language:** `{hunt_config.language}`\n\n" + markdown += "## Query\n\n" + markdown += f"```sql\n{hunt_config.query}```\n\n" + if hunt_config.notes: + markdown += "## Notes\n\n" + "\n".join(f"- {note}" for note in hunt_config.notes) + if hunt_config.mitre: + markdown += "\n## MITRE ATT&CK Techniques\n\n" + "\n".join( + f"- [{tech}](https://atlas.mitre.org/techniques/{tech})\n" for tech in hunt_config.mitre + ) + if hunt_config.references: + markdown += "\n## References\n\n" + "\n".join(f"- {ref}" for ref in hunt_config.references) + markdown += f"\n- [{hunt_config.name}]({Path('../queries') / file_path.name})" + return markdown + + +def process_toml_files(base_path: Path) -> None: + """Process all TOML files in the directory recursively and convert them to Markdown.""" + hunts = load_all_toml(base_path) + for hunt_config, toml_file in hunts: + markdown_content = convert_toml_to_markdown(hunt_config, toml_file) + markdown_path = toml_file.parent.parent / "docs" / f"{toml_file.stem}.md" + markdown_path.parent.mkdir(parents=True, exist_ok=True) + markdown_path.write_text(markdown_content, encoding="utf-8") + print(f"Markdown generated: {markdown_path}") + + +if __name__ == "__main__": + process_toml_files(HUNTING_DIR) diff --git a/hunting/index.md b/hunting/index.md new file mode 100644 index 000000000..5fb35f16e --- /dev/null +++ b/hunting/index.md @@ -0,0 +1,8 @@ +# List of Available Queries + +Here are the queries currently available: + +## llm +- [Denial of Service or Resource Exhaustion Attacks Detection](llm/queries/docs/llm_dos_resource_exhaustion_detection.md) (ES|QL) +- [Monitoring for Latency Anomalies](llm/queries/docs/llm_latency_anomalies_detection.md) (ES|QL) +- [Sensitive Content Refusal Detection](llm/queries/docs/llm_sensitive_content_refusal_detection.md) (ES|QL) diff --git a/hunting/llm/README.md b/hunting/llm/README.md new file mode 100644 index 000000000..80d5cd20c --- /dev/null +++ b/hunting/llm/README.md @@ -0,0 +1,43 @@ +# LLM Threat Hunting Queries + +Welcome to the `LLM` subfolder within the `hunting` directory of the `detection-rules` repository. This specialized section is dedicated to threat hunting queries designed for Large Language Model (LLM) applications, targeting the unique security challenges these systems face. + +## Emphasis on OWASP Top 10 for LLMs + +Our queries are developed with a keen awareness of the [OWASP Top 10 risks for Large Language Model Applications](https://owasp.org/www-project-top-10-for-large-language-model-applications/). This crucial resource outlines the predominant security risks for LLMs, guiding our efforts in crafting queries that proactively address these vulnerabilities and ensure comprehensive threat mitigation. + +## Emphasis on MITRE ATLAS + +The [ATLAS Matrix](https://atlas.mitre.org/matrices/ATLAS/) covers the progression of tactics used in attacks with ML techniques belonging to different tactics. + +- Reconnaissance +- Resource Development +- Initial Access +- ML Model Access +- Execution +- Persistence +- Privilege Escalation +- Defense Evasion +- Credential Access +- Discovery +- Collection +- ML Attack Staging +- Exfiltration +- Impact + +## Scope of Threats and Protections + +The queries in this folder are tailored to monitor and protect against a broad spectrum of threats to LLMs: + +- **Sensitive Content Refusal**: Monitors LLM interactions to ensure compliance with ethical standards, particularly in refusing to process sensitive topics. +- **Denial of Service (DoS) and Resource Exhaustion**: Aims to prevent disruptions in LLM operations by detecting patterns indicative of DoS attacks or resource exhaustion scenarios. +- **Latency Anomalies**: Tracks processing delays that could signal underlying performance issues or security threats, maintaining operational efficiency and safeguarding against potential attacks like DDoS. + +### Benefits of These Queries + +These queries assist organizations in: +- Detecting and mitigating misuse or attacks that threaten data integrity or disrupt services. +- Ensuring that LLMs adhere strictly to operational and ethical boundaries through continuous monitoring. +- Maintaining high performance and reliability of LLMs by preemptively identifying and resolving factors that cause inefficiencies. + +For more details, read our blog on [LLM Detections](https://www.elastic.co/security-labs/elastic-advances-llm-security). \ No newline at end of file diff --git a/hunting/llm/docs/llm_dos_resource_exhaustion_detection.md b/hunting/llm/docs/llm_dos_resource_exhaustion_detection.md new file mode 100644 index 000000000..a48dbf0d1 --- /dev/null +++ b/hunting/llm/docs/llm_dos_resource_exhaustion_detection.md @@ -0,0 +1,44 @@ +# Denial of Service or Resource Exhaustion Attacks Detection + +--- + +## Metadata + +- **Author:** Elastic +- **UUID:** `dc181967-c32c-46c9-b84b-ec4c8811c6a0` +- **Integration:** `aws_bedrock.invocation` +- **Language:** `ES|QL` + +## Query + +```sql +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + AND ( + gen_ai.usage.prompt_tokens > 8000 OR + gen_ai.usage.completion_tokens > 8000 or + gen_ai.performance.request_size > 8000 + ) + | STATS max_prompt_tokens = max(gen_ai.usage.prompt_tokens), + max_request_tokens = max(gen_ai.performance.request_size), + max_completion_tokens = max(gen_ai.usage.completion_tokens), + request_count = count() BY cloud.account.id + | WHERE request_count > 1 + | SORT max_prompt_tokens, max_request_tokens, max_completion_tokens DESC +``` + +## Notes + +- This query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks. +- Consider reviewing the context of high token requests to differentiate between legitimate heavy usage and potential abuse. Monitor the source of requests and patterns over time for better assessment. +- Ensure logging and monitoring are correctly configured to capture detailed metrics on token usage. This will facilitate accurate detection and allow for a quick response to potential threats. +- Collect evidence from logs that detail the timestamp, user ID, session information, and token counts for incidents flagged by this analytic. This information will be crucial for forensic analysis in the event of a security incident. +## MITRE ATT&CK Techniques + +- [AML.T0034](https://atlas.mitre.org/techniques/AML.T0034) + +## References + +- https://www.elastic.co/security-labs/elastic-advances-llm-security +- https://owasp.org/www-project-top-10-for-large-language-model-applications/ +- [Denial of Service or Resource Exhaustion Attacks Detection](../queries/llm_dos_resource_exhaustion_detection.toml) \ No newline at end of file diff --git a/hunting/llm/docs/llm_latency_anomalies_detection.md b/hunting/llm/docs/llm_latency_anomalies_detection.md new file mode 100644 index 000000000..68d3ce21a --- /dev/null +++ b/hunting/llm/docs/llm_latency_anomalies_detection.md @@ -0,0 +1,39 @@ +# Monitoring for Latency Anomalies + +--- + +## Metadata + +- **Author:** Elastic +- **UUID:** `3708787b-811b-43b1-b2e7-c7276b8db48c` +- **Integration:** `aws_bedrock.invocation` +- **Language:** `ES|QL` + +## Query + +```sql +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + | EVAL response_delay_seconds = gen_ai.performance.start_response_time / 1000 + | WHERE response_delay_seconds > 5 + | STATS max_response_delay = max(response_delay_seconds), + request_count = count() BY gen_ai.user.id + | WHERE request_count > 3 + | SORT max_response_delay DESC +``` + +## Notes + +- This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies. +- Review the incidents flagged by this analytic to understand the context and potential sources of latency. This can include network configurations, resource allocation, or external network pressures. +- Effective logging and monitoring setup are essential to capture relevant latency metrics accurately. Ensure system clocks and time syncing are properly configured to avoid false positives. +- Gather comprehensive logs that detail the request and response timestamps, user IDs, and session details for thorough investigation and evidence collection in case of security incidents. +## MITRE ATT&CK Techniques + +- [AML.T0029](https://atlas.mitre.org/techniques/AML.T0029) + +## References + +- https://www.elastic.co/security-labs/elastic-advances-llm-security +- https://owasp.org/www-project-top-10-for-large-language-model-applications/ +- [Monitoring for Latency Anomalies](../queries/llm_latency_anomalies_detection.toml) \ No newline at end of file diff --git a/hunting/llm/docs/llm_sensitive_content_refusal_detection.md b/hunting/llm/docs/llm_sensitive_content_refusal_detection.md new file mode 100644 index 000000000..fa880f41f --- /dev/null +++ b/hunting/llm/docs/llm_sensitive_content_refusal_detection.md @@ -0,0 +1,39 @@ +# Sensitive Content Refusal Detection + +--- + +## Metadata + +- **Author:** Elastic +- **UUID:** `8fabae86-7ed2-4006-9623-5db28164f374` +- **Integration:** `aws_bedrock.invocation` +- **Language:** `ES|QL` + +## Query + +```sql +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + AND ( + gen_ai.completion LIKE "*I cannot provide any information about*" + AND gen_ai.completion LIKE "*end_turn*" + ) + | STATS user_request_count = count() BY gen_ai.user.id + | WHERE user_request_count >= 3 +``` + +## Notes + +- This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards. +- Examine flagged interactions for patterns or anomalies in user requests that may indicate malicious intent or probing of model boundaries. +- Regularly review and update the phrases that trigger refusals to adapt to new ethical guidelines and compliance requirements. +- Ensure that data logs contain enough detail to provide context around the refusal, which will aid in subsequent investigations by security teams. +## MITRE ATT&CK Techniques + +- [AML.T0051](https://atlas.mitre.org/techniques/AML.T0051) + +## References + +- https://www.elastic.co/security-labs/elastic-advances-llm-security +- https://owasp.org/www-project-top-10-for-large-language-model-applications/ +- [Sensitive Content Refusal Detection](../queries/llm_sensitive_content_refusal_detection.toml) \ No newline at end of file diff --git a/hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml b/hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml new file mode 100644 index 000000000..618fbbd4c --- /dev/null +++ b/hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml @@ -0,0 +1,30 @@ +[hunt] +author = "Elastic" +integration = "aws_bedrock.invocation" +uuid = "dc181967-c32c-46c9-b84b-ec4c8811c6a0" +name = "Denial of Service or Resource Exhaustion Attacks Detection" +language = "ES|QL" +query = ''' +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + AND ( + gen_ai.usage.prompt_tokens > 8000 OR + gen_ai.usage.completion_tokens > 8000 or + gen_ai.performance.request_size > 8000 + ) + | STATS max_prompt_tokens = max(gen_ai.usage.prompt_tokens), + max_request_tokens = max(gen_ai.performance.request_size), + max_completion_tokens = max(gen_ai.usage.completion_tokens), + request_count = count() BY cloud.account.id + | WHERE request_count > 1 + | SORT max_prompt_tokens, max_request_tokens, max_completion_tokens DESC +''' +notes = [ + "This query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks.", + "Consider reviewing the context of high token requests to differentiate between legitimate heavy usage and potential abuse. Monitor the source of requests and patterns over time for better assessment.", + "Ensure logging and monitoring are correctly configured to capture detailed metrics on token usage. This will facilitate accurate detection and allow for a quick response to potential threats.", + "Collect evidence from logs that detail the timestamp, user ID, session information, and token counts for incidents flagged by this analytic. This information will be crucial for forensic analysis in the event of a security incident." +] +mitre = ["AML.T0034"] +references = ["https://www.elastic.co/security-labs/elastic-advances-llm-security", + "https://owasp.org/www-project-top-10-for-large-language-model-applications/"] diff --git a/hunting/llm/queries/llm_latency_anomalies_detection.toml b/hunting/llm/queries/llm_latency_anomalies_detection.toml new file mode 100644 index 000000000..a1e110a39 --- /dev/null +++ b/hunting/llm/queries/llm_latency_anomalies_detection.toml @@ -0,0 +1,25 @@ +[hunt] +author = "Elastic" +integration = "aws_bedrock.invocation" +uuid = "3708787b-811b-43b1-b2e7-c7276b8db48c" +name = "Monitoring for Latency Anomalies" +language = "ES|QL" +query = ''' +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + | EVAL response_delay_seconds = gen_ai.performance.start_response_time / 1000 + | WHERE response_delay_seconds > 5 + | STATS max_response_delay = max(response_delay_seconds), + request_count = count() BY gen_ai.user.id + | WHERE request_count > 3 + | SORT max_response_delay DESC +''' +notes = [ + "This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies.", + "Review the incidents flagged by this analytic to understand the context and potential sources of latency. This can include network configurations, resource allocation, or external network pressures.", + "Effective logging and monitoring setup are essential to capture relevant latency metrics accurately. Ensure system clocks and time syncing are properly configured to avoid false positives.", + "Gather comprehensive logs that detail the request and response timestamps, user IDs, and session details for thorough investigation and evidence collection in case of security incidents." +] +mitre = ["AML.T0029"] +references = ["https://www.elastic.co/security-labs/elastic-advances-llm-security", + "https://owasp.org/www-project-top-10-for-large-language-model-applications/"] diff --git a/hunting/llm/queries/llm_sensitive_content_refusal_detection.toml b/hunting/llm/queries/llm_sensitive_content_refusal_detection.toml new file mode 100644 index 000000000..358d5c6e3 --- /dev/null +++ b/hunting/llm/queries/llm_sensitive_content_refusal_detection.toml @@ -0,0 +1,25 @@ +[hunt] +author = "Elastic" +integration = "aws_bedrock.invocation" +uuid = "8fabae86-7ed2-4006-9623-5db28164f374" +name = "Sensitive Content Refusal Detection" +language = "ES|QL" +query = ''' +from logs-aws_bedrock.invocation-* + | WHERE @timestamp > NOW() - 1 DAY + AND ( + gen_ai.completion LIKE "*I cannot provide any information about*" + AND gen_ai.completion LIKE "*end_turn*" + ) + | STATS user_request_count = count() BY gen_ai.user.id + | WHERE user_request_count >= 3 +''' +notes = [ + "This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards.", + "Examine flagged interactions for patterns or anomalies in user requests that may indicate malicious intent or probing of model boundaries.", + "Regularly review and update the phrases that trigger refusals to adapt to new ethical guidelines and compliance requirements.", + "Ensure that data logs contain enough detail to provide context around the refusal, which will aid in subsequent investigations by security teams." +] +mitre = ["AML.T0051"] +references = ["https://www.elastic.co/security-labs/elastic-advances-llm-security", + "https://owasp.org/www-project-top-10-for-large-language-model-applications/"] diff --git a/tests/test_hunt_data.py b/tests/test_hunt_data.py new file mode 100644 index 000000000..4fd83254e --- /dev/null +++ b/tests/test_hunt_data.py @@ -0,0 +1,65 @@ +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. + +"""Test for hunt toml files.""" +import unittest + +from hunting.generate_markdown import HUNTING_DIR, load_toml + + +class TestHunt(unittest.TestCase): + """Test hunt toml files.""" + + def test_toml_loading(self): + """Test loading a hunt toml file content.""" + example_toml = """ + [hunt] + author = "Elastic" + integration = "aws_bedrock.invocation" + uuid = "dc181967-c32c-46c9-b84b-ec4c8811c6a0" + name = "Denial of Service or Resource Exhaustion Attacks Detection" + language = "ES|QL" + query = 'SELECT * FROM logs' + notes = ["High token usage can strain system resources."] + mitre = ["AML.T0034"] + references = ["https://www.elastic.co"] + """ + config = load_toml(example_toml) + self.assertEqual(config.author, "Elastic") + self.assertEqual(config.integration, "aws_bedrock.invocation") + self.assertEqual(config.uuid, "dc181967-c32c-46c9-b84b-ec4c8811c6a0") + self.assertEqual( + config.name, "Denial of Service or Resource Exhaustion Attacks Detection" + ) + self.assertEqual(config.language, "ES|QL") + + def test_load_toml_files(self): + """Test loading and validating all Hunt TOML files in the hunting directory.""" + + for toml_file in HUNTING_DIR.rglob("*.toml"): + toml_contents = toml_file.read_text() + hunt = load_toml(toml_contents) + self.assertTrue(hunt.author) + self.assertTrue(hunt.integration) + self.assertTrue(hunt.uuid) + self.assertTrue(hunt.name) + self.assertTrue(hunt.language) + self.assertTrue(hunt.query) + + def test_markdown_existence(self): + """Ensure each TOML file has a corresponding Markdown file in the docs directory.""" + for toml_file in HUNTING_DIR.rglob("*.toml"): + expected_markdown_path = ( + toml_file.parent.parent / "docs" / toml_file.with_suffix(".md").name + ) + + self.assertTrue( + expected_markdown_path.exists(), + f"Markdown file not found for {toml_file} at expected location {expected_markdown_path}", + ) + + +if __name__ == "__main__": + unittest.main()