diff --git a/.gitignore b/.gitignore index 3ef4a4930..92f47346b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ ###### Place new entries directly below this line! ###### +CLAUDE.md + # Ignore anything in the ./.tmp directory .tmp/ diff --git a/kustomize/README.md b/kustomize/README.md index 1f9be713b..b63230d94 100644 --- a/kustomize/README.md +++ b/kustomize/README.md @@ -19,7 +19,7 @@ limitations under the License. ## Install and Run Locally -One can run ExploitIQ on his local machine ( No GPU dependency is required!), for the purpose of testing, debugging and troubleshooting problems: +One can run the RHTPA exploit intelligence workflow on his local machine ( No GPU dependency is required!), for the purpose of testing, debugging and troubleshooting problems: 1. Install the lightweight [uv package manager](https://docs.astral.sh/uv/getting-started/installation). 2. Ensure Python 3.12 is installed for your operating system. @@ -98,7 +98,7 @@ export USE_CONTAINER_SOURCES=true ## Deploy And Run On OCP -1. Create a `base/secrets.env` file containing the API keys for external services `ExploitIQ` might use. Not all keys are mandatory. Refer to the main [README](../README.md#obtain-api-keys) for details on how to create the Red Hat credentials and other API keys. +1. Create a `base/secrets.env` file containing the API keys for external services `RHTPA exploit intelligence` might use. Not all keys are mandatory. Refer to the main [README](../README.md#obtain-api-keys) for details on how to create the Red Hat credentials and other API keys. ```shell cat > base/secrets.env << EOF @@ -128,7 +128,7 @@ argilla_api_key=your_argilla_api_key EOF ``` -4. Create an image pull secret to authorize pulling the `ExploitIQ` and `Argilla` container images: +4. Create an image pull secret to authorize pulling the `exploit-intelligence` and `Argilla` container images: ```shell oc create secret generic exploit-iq-pull-secret --from-file=.dockerconfigjson= --type=kubernetes.io/dockerconfigjson @@ -152,7 +152,7 @@ EOF >[!IMPORTANT] >This secret is essential for product scanning to authenticate and pull component images. If you skip this step, kustomize will still deploy, but authenticated pulls will not work until you provide real credentials. -6. Create the `oauth-secret.env` file containing the `client-secret` and `openshift-domain` values required by the [ExploitIQ Client](./base/exploit_iq_client.yaml) configuration. +6. Create the `oauth-secret.env` file containing the `client-secret` and `openshift-domain` values required by the [exploit-intelligence-client](./base/exploit_iq_client.yaml) configuration. If openshift resource of kind `OAuthClient` named `exploit-iq-client` exists, just get the secret from there: ```shell @@ -187,7 +187,7 @@ exploit-iq-password=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32) EOF ``` -8. Update `ExploitIQ` configuration file with the correct callback URL for the client service. +8. Update exploit intelligence configuration file with the correct callback URL for the client service. ```shell export CALLBACK_URL="https://exploit-iq-client.$(oc project -q).svc:8443" @@ -196,7 +196,7 @@ find . -type f -name 'exploit-iq-config.yml' -exec sed -i "s|CALLBACK_URL_PLACEH ### Configuring Git SSL Certificate Authority for Custom CAs -If your Git server uses a certificate that is signed by a custom Certificate Authority (CA), you must provide the CA certificate bundle to enable ExploitIQ to verify the Git server identity. +If your Git server uses a certificate that is signed by a custom Certificate Authority (CA), you must provide the CA certificate bundle to enable the exploit intelligence workflow to verify the Git server identity. > [!IMPORTANT] > If you need to access Red Hat internal Git repositories such as `gitlab.cee.redhat.com`, you must complete this procedure. @@ -245,15 +245,15 @@ openssl crl2pkcs7 -nocrl -certfile kustomize/base/ca-certs/ca-bundle.crt | \ >[!IMPORTANT] You should only run one of the steps 9,10 or 11, depending on if you want to run the service with a self hosted LLM, self hosted LLM with MLOps or Nvidia remote NIM. -9. To deploy `ExploitIQ` with a self-hosted LLM , run: +9. To deploy the exploit intelligence service with a self-hosted LLM , run: ```shell -# Deploy ExploitIQ with self hosted llama3.1-70b-4bit LLM +# Deploy exploit intelligence with self hosted llama3.1-70b-4bit LLM oc kustomize overlays/self-hosted-llama3.1-70b-4bit | oc apply -f - -n $YOUR_NAMESPACE_NAME ``` -10. To deploy `ExploitIQ` with a self-hosted LLM and MLOps, run: +10. To deploy the exploit intelligence service with a self-hosted LLM and MLOps, run: ```shell # Patch overlay kustomization yaml with deployment namespace value (Grafana and Tempo) @@ -262,12 +262,12 @@ sed -i "s/REPLACE_NAMESPACE/$YOUR_NAMESPACE_NAME/" overlays/mlops/tempo/kustomiz ``` ```shell -# replace EXPLOIT_IQ_GRAFANA_SA_TOKEN with ExploitIQ Grafana SA Token from bitwarden vault (1 year expiration date) +# replace EXPLOIT_IQ_GRAFANA_SA_TOKEN with exploit intelligence Grafana SA Token from bitwarden vault (1 year expiration date) oc create secret generic grafana-bearer-token --from-literal=token='EXPLOIT_IQ_GRAFANA_SA_TOKEN' ``` ```shell -# Deploy ExploitIQ with self hosted llama3.1-70b-4bit LLM and MLOps +# Deploy exploit intelligence with self hosted llama3.1-70b-4bit LLM and MLOps oc kustomize overlays/mlops | oc apply -f - -n $YOUR_NAMESPACE_NAME ``` @@ -299,9 +299,9 @@ oc kustomize overlays/mlops \ ``` -10. Alternatively, to deploy `ExploitIQ` with a fully remote nim LLM, run: +10. Alternatively, to deploy the exploit intelligence service with a fully remote nim LLM, run: ```shell -# Deploy ExploitIQ with remote nim llama-3.1-70b-16bit LLM +# Deploy exploit intelligence with remote nim llama-3.1-70b-16bit LLM oc kustomize overlays/remote-nim-all | oc apply -f - -n $YOUR_NAMESPACE_NAME ``` >[!WARNING] @@ -335,7 +335,7 @@ openshift-domain=$(oc get dns cluster -o jsonpath='{.spec.baseDomain}') EOF ``` -12. **(Optional) Enable OAuth for the ExploitIQ MCP Server.** If you want MCP clients (Claude Code, Cursor, etc.) to authenticate via OpenShift OAuth, create an `OAuthClient` CR for the MCP server: +12. **(Optional) Enable OAuth for the exploit intelligence MCP Server.** If you want MCP clients (Claude Code, Cursor, etc.) to authenticate via OpenShift OAuth, create an `OAuthClient` CR for the MCP server: ```bash oc create -f - < str: return f"{function_file_name};{function_name_to_search}" diff --git a/src/exploit_iq_commons/utils/dep_tree.py b/src/exploit_iq_commons/utils/dep_tree.py index e98ed0dc2..f18b9ef68 100644 --- a/src/exploit_iq_commons/utils/dep_tree.py +++ b/src/exploit_iq_commons/utils/dep_tree.py @@ -75,7 +75,7 @@ def _get_go_repo_lock(manifest_path) -> threading.Lock: return _go_repo_locks[key] -ROOT_LEVEL_SENTINEL = 'root-top-level-agent-morpheus' +ROOT_LEVEL_SENTINEL = 'root-top-level-exploit-intelligence' TRANSITIVE_ENV_NAME = 'transitive_env' INSTALLED_PACKAGES_FILE = 'installed_packages.txt' diff --git a/src/exploit_iq_commons/utils/functions_parsers/java_functions_parsers.py b/src/exploit_iq_commons/utils/functions_parsers/java_functions_parsers.py index 0bd51f3be..2165aa584 100644 --- a/src/exploit_iq_commons/utils/functions_parsers/java_functions_parsers.py +++ b/src/exploit_iq_commons/utils/functions_parsers/java_functions_parsers.py @@ -28,7 +28,7 @@ strip_java_generics, JAVA_ANNOTATION_SYMBOL, extract_fqcn from exploit_iq_commons.logging.loggers_factory import LoggingFactory -logger = LoggingFactory.get_agent_logger(f"morpheus.{__name__}") +logger = LoggingFactory.get_agent_logger(f"exploit-intelligence.{__name__}") PARAMETER = "parameter" diff --git a/src/exploit_iq_commons/utils/java_chain_of_calls_retriever.py b/src/exploit_iq_commons/utils/java_chain_of_calls_retriever.py index 1b2a8e6d5..c2fabf8f5 100644 --- a/src/exploit_iq_commons/utils/java_chain_of_calls_retriever.py +++ b/src/exploit_iq_commons/utils/java_chain_of_calls_retriever.py @@ -36,7 +36,7 @@ create_inheritance_map, get_target_class_names, dummy_package_name from exploit_iq_commons.data_models.input import SourceDocumentsInfo -logger = LoggingFactory.get_agent_logger(f"morpheus.{__name__}") +logger = LoggingFactory.get_agent_logger(f"exploit-intelligence.{__name__}") # Lowercase package segments; class segments start with uppercase; allow dots or $ for inners _FQCN_STRICT_RE = re.compile( diff --git a/src/exploit_iq_commons/utils/transitive_code_searcher_tool.py b/src/exploit_iq_commons/utils/transitive_code_searcher_tool.py index 12c3455a3..09956c637 100644 --- a/src/exploit_iq_commons/utils/transitive_code_searcher_tool.py +++ b/src/exploit_iq_commons/utils/transitive_code_searcher_tool.py @@ -25,7 +25,7 @@ from exploit_iq_commons.logging.loggers_factory import LoggingFactory, MULTI_LINE_MESSAGE_TRUE -logger = LoggingFactory.get_agent_logger(f"morpheus.{__name__}") +logger = LoggingFactory.get_agent_logger(f"exploit-intelligence.{__name__}") class TransitiveCodeSearcher: diff --git a/src/vuln_analysis/register.py b/src/vuln_analysis/register.py index eaeef6039..0c3d919ce 100644 --- a/src/vuln_analysis/register.py +++ b/src/vuln_analysis/register.py @@ -575,7 +575,7 @@ async def call_llm_engine_subgraph_node(message: AgentMorpheusEngineInput): graph = graph_builder.compile() #graph.get_graph().draw_mermaid_png(output_file_path="checker_flow.png") - def convert_str_to_agent_morpheus_input(input: str) -> AgentMorpheusInput: + def convert_str_to_exploit_intelligence_input(input: str) -> AgentMorpheusInput: logger.debug("Converting JSON string input to AgentMorpheusInput (length: %d)", len(input)) try: return AgentMorpheusInput.model_validate_json(input) @@ -583,7 +583,7 @@ def convert_str_to_agent_morpheus_input(input: str) -> AgentMorpheusInput: logger.error("Failed to convert input to AgentMorpheusInput: %s. Your input needs to be a json string.", e) raise e - def convert_textio_to_agent_morpheus_input(input: TextIOWrapper) -> AgentMorpheusInput: + def convert_textio_to_exploit_intelligence_input(input: TextIOWrapper) -> AgentMorpheusInput: logger.debug("Converting TextIOWrapper input to AgentMorpheusInput") try: data = input.read() @@ -593,7 +593,7 @@ def convert_textio_to_agent_morpheus_input(input: TextIOWrapper) -> AgentMorpheu "Failed to convert input to AgentMorpheusInput: %s. Your input needs to be a TextIOWrapper object.", e) raise e - def convert_agent_morpheus_output_to_str(output: AgentMorpheusOutput) -> str: + def convert_exploit_intelligence_output_to_str(output: AgentMorpheusOutput) -> str: logger.debug("Converting AgentMorpheusOutput to JSON string") try: return output.model_dump_json() @@ -612,9 +612,9 @@ async def _response_fn(input_message: AgentMorpheusInput) -> AgentMorpheusOutput description=config.description, input_schema=AgentMorpheusInput, converters=[ - convert_str_to_agent_morpheus_input, - convert_textio_to_agent_morpheus_input, - convert_agent_morpheus_output_to_str + convert_str_to_exploit_intelligence_input, + convert_textio_to_exploit_intelligence_input, + convert_exploit_intelligence_output_to_str ]) except GeneratorExit: logger.info("Workflow exited early!") diff --git a/src/vuln_analysis/utils/function_name_extractor.py b/src/vuln_analysis/utils/function_name_extractor.py index b3d19e467..eca13427b 100644 --- a/src/vuln_analysis/utils/function_name_extractor.py +++ b/src/vuln_analysis/utils/function_name_extractor.py @@ -20,7 +20,7 @@ from exploit_iq_commons.logging.loggers_factory import LoggingFactory -logger = LoggingFactory.get_agent_logger(f"morpheus.{__name__}") +logger = LoggingFactory.get_agent_logger(f"exploit-intelligence.{__name__}") def traverse_all_parameters(function_ending_index_end, function_prefix_index_end, function_string): diff --git a/src/vuln_analysis/utils/function_name_locator.py b/src/vuln_analysis/utils/function_name_locator.py index 4530ec24a..89054202b 100644 --- a/src/vuln_analysis/utils/function_name_locator.py +++ b/src/vuln_analysis/utils/function_name_locator.py @@ -25,7 +25,7 @@ from exploit_iq_commons.utils.source_rpm_downloader import RPMDependencyManager from vuln_analysis.utils.prompt_factory import FL_EXAMPLES -logger = LoggingFactory.get_agent_logger(f"morpheus.{__name__}") +logger = LoggingFactory.get_agent_logger(f"exploit-intelligence.{__name__}") class FunctionNameLocator: diff --git a/src/vuln_analysis/utils/llm_engine_utils.py b/src/vuln_analysis/utils/llm_engine_utils.py index 1857bdd89..6ec5de30e 100644 --- a/src/vuln_analysis/utils/llm_engine_utils.py +++ b/src/vuln_analysis/utils/llm_engine_utils.py @@ -171,7 +171,7 @@ def _build_full_pipeline_details_md(patch_result: WebPatchResult | dict | None) return result or None -def parse_agent_morpheus_engine_output(vuln_id: str, +def parse_exploit_intelligence_engine_output(vuln_id: str, checklist_results: list[dict[str, typing.Any]], summary: str, justification: dict[str, str], @@ -362,7 +362,7 @@ def postprocess_engine_output(message: AgentMorpheusEngineInput, justification = result.justifications[vuln_id] is_vulnerable = justification.get("affected_status") == "TRUE" output.append( - parse_agent_morpheus_engine_output(vuln_id=vuln_id, + parse_exploit_intelligence_engine_output(vuln_id=vuln_id, checklist_results=result.checklist_results[vuln_id], summary=result.final_summaries[vuln_id], justification=justification, diff --git a/src/vuln_analysis/utils/vex/implementations/csaf_generator.py b/src/vuln_analysis/utils/vex/implementations/csaf_generator.py index 605c37192..cd2cf42b2 100644 --- a/src/vuln_analysis/utils/vex/implementations/csaf_generator.py +++ b/src/vuln_analysis/utils/vex/implementations/csaf_generator.py @@ -47,9 +47,9 @@ NOTE_TITLE_VULNERABILITY_DESCRIPTION = "Vulnerability description" NOTE_TITLE_VULNERABILITY_SUMMARY = "Vulnerability summary" NOTE_TITLE_RHSA_STATEMENT = "Red Hat Security Advisory Statement" -NOTE_TITLE_EXPLOITIQ_SUMMARY = "ExploitIQ Analysis Summary" -NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_REASONING = "ExploitIQ Analysis Justification Reasoning" -NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_LABEL = "ExploitIQ Analysis Justification Label" +NOTE_TITLE_EXPLOITIQ_SUMMARY = "RHTPA exploit intelligence Analysis Summary" +NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_REASONING = "RHTPA exploit intelligence Analysis Justification Reasoning" +NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_LABEL = "RHTPA exploit intelligence Analysis Justification Label" NOTE_TITLE_UNOFFICIAL_CONTENT = "Unofficial Content Notice" # Disclaimer text @@ -138,7 +138,7 @@ def _enrich_vulnerabilities_with_notes( "title": NOTE_TITLE_RHSA_STATEMENT }) - # Add ExploitIQ analysis summary + # Add RHTPA exploit intelligence Analysis Summary summary = final_summaries.get(vuln_id) notes.append({ "category": NOTE_CATEGORY_OTHER, @@ -176,7 +176,7 @@ def generate(self, state: AgentMorpheusEngineState) -> Dict[str, Any]: product_name = message.input.image.name product_tag = message.input.image.tag - csaf_gen.set_header_title(f"ExploitIQ VEX Document - {product_name}{"@" if OCI_DIGEST_RE.fullmatch(product_tag) else ":"}{product_tag}") + csaf_gen.set_header_title(f"RHTPA exploit intelligence VEX Document - {product_name}{"@" if OCI_DIGEST_RE.fullmatch(product_tag) else ":"}{product_tag}") csaf_gen.set_value("notes",[ { diff --git a/src/vuln_analysis/utils/vex/tests/test_csaf_generator_integration.py b/src/vuln_analysis/utils/vex/tests/test_csaf_generator_integration.py index 5bca102d3..53f0dfe37 100644 --- a/src/vuln_analysis/utils/vex/tests/test_csaf_generator_integration.py +++ b/src/vuln_analysis/utils/vex/tests/test_csaf_generator_integration.py @@ -119,7 +119,7 @@ def test_document_has_correct_title(self, mock_state): result = generator.generate(mock_state) title = result["document"].get("title") - assert "ExploitIQ VEX Document - " + _DEFAULT_PRODUCT_NAME + ":" + _DEFAULT_PRODUCT_TAG in title + assert "RHTPA exploit intelligence VEX Document - " + _DEFAULT_PRODUCT_NAME + ":" + _DEFAULT_PRODUCT_TAG in title def test_oci_digest_tag_uses_at_separator(self): """Test that OCI digest tags use @ separator instead of : in title.""" @@ -132,7 +132,7 @@ def test_oci_digest_tag_uses_at_separator(self): result = generator.generate(state) title = result["document"].get("title") - assert "ExploitIQ VEX Document - " + _DEFAULT_PRODUCT_NAME + "@" + oci_digest in title + assert "RHTPA exploit intelligence VEX Document - " + _DEFAULT_PRODUCT_NAME + "@" + oci_digest in title def test_document_has_disclaimer_note(self, mock_state): """Test that document includes the disclaimer note.""" diff --git a/tests/test_vex_csaf_helpers.py b/tests/test_vex_csaf_helpers.py index 687e52473..84a2ade9a 100644 --- a/tests/test_vex_csaf_helpers.py +++ b/tests/test_vex_csaf_helpers.py @@ -21,7 +21,8 @@ from exploit_iq_commons.data_models.cve_intel import CveIntel, CveIntelGhsa, CveIntelRhsa from vuln_analysis.utils.vex.implementations.csaf_generator import ( - _enrich_vulnerabilities_with_notes, + _enrich_vulnerabilities_with_notes, NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_REASONING, NOTE_TITLE_EXPLOITIQ_SUMMARY, + NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_LABEL, ) @@ -94,7 +95,7 @@ def test_adds_analysis_summary_note(self, base_csaf_json, base_intel_map, base_j _enrich_vulnerabilities_with_notes(base_csaf_json, base_intel_map, final_summaries, base_justifications) notes = base_csaf_json["vulnerabilities"][0]["notes"] - analysis_notes = [n for n in notes if n.get("title") == "ExploitIQ Analysis Summary"] + analysis_notes = [n for n in notes if n.get("title") == NOTE_TITLE_EXPLOITIQ_SUMMARY] assert len(analysis_notes) == 1 assert analysis_notes[0]["text"] == "This is the analysis summary" assert analysis_notes[0]["category"] == "other" @@ -112,11 +113,11 @@ def test_adds_justification_notes(self, base_csaf_json, base_intel_map, base_fin notes = base_csaf_json["vulnerabilities"][0]["notes"] - reasoning_notes = [n for n in notes if n.get("title") == "ExploitIQ Analysis Justification Reasoning"] + reasoning_notes = [n for n in notes if n.get("title") == NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_REASONING] assert len(reasoning_notes) == 1 assert reasoning_notes[0]["text"] == "The vulnerable code path is reachable" - label_notes = [n for n in notes if n.get("title") == "ExploitIQ Analysis Justification Label"] + label_notes = [n for n in notes if n.get("title") == NOTE_TITLE_EXPLOITIQ_JUSTIFICATION_LABEL] assert len(label_notes) == 1 assert label_notes[0]["text"] == "vulnerable"