Skip to content

feat(cyber-guardian-agent): migrate recipe to contrib/python and align with repo standards - #2776

Open
abhilash-adavi wants to merge 9 commits into
google:mainfrom
abhilash-adavi:feat/migrate-cyber-guardian-agent
Open

abhilash-adavi wants to merge 9 commits into
google:mainfrom
abhilash-adavi:feat/migrate-cyber-guardian-agent

Conversation

@abhilash-adavi

Copy link
Copy Markdown

Summary

Migrates the cyber-guardian-agent recipe from python/agents/cyber-guardian-agent to contrib/python/cyber-guardian-agent and updates it to meet all repository baseline standards.

Changes

  • Directory & Package Layout: Moved recipe to contrib/python/cyber-guardian-agent with standardized app/ package structure, Dockerfile, and .dockerignore.
  • Manifest Metadata: Added manifest.yaml with ownership (team, POC, and contributors), architecture, dependencies, and tags.
  • Model & Environment Variables: Upgraded model to gemini-3.5-flash configured via MODEL_NAME in .env.example with load_dotenv() in app/__init__.py (no hardcoded model literals in source).
  • No Import-Time Side Effects: Replaced module-import-time BigQuery table initialization and ADC lookups with lazy runtime initialization on first tool invocation.
  • Testing & Linting: Added tests/test_runnability.py and tests/conftest.py; formatted and linted with ruff.

Verification

  • uv run python tools/validate.py all contrib/python/cyber-guardian-agent — Passed
  • uv run ruff check && uv run ruff format --check — Passed
  • uv run --directory contrib/python/cyber-guardian-agent pytest -v — Passed
  • End-to-end multi-agent execution verified against Vertex AI (gemini-3.5-flash) and BigQuery.

…n with repo standards

- Move cyber-guardian-agent recipe to contrib/python/cyber-guardian-agent with app/ package layout
- Add manifest.yaml with ownership, architecture, dependencies, and tags metadata
- Add Dockerfile, .dockerignore, and runnability test suite (tests/test_runnability.py, tests/conftest.py)
- Upgrade model to gemini-3.5-flash read via MODEL_NAME from environment with load_dotenv() bootstrap
- Remove import-time BigQuery table initialization and ADC lookup in favor of lazy runtime initialization

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated House Rules review — 1 finding(s).

Also, on lines this PR does not change:

  • contrib/python/cyber-guardian-agent/app/tools.py:1 — [MINOR] the .py files carry two different headers: 0 a shorter notice, 16 none, and 2 the full Apache block (contrib/python/cyber-guardian-agent/app/tools.py; and 5 more)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Hygiene review — 3 finding(s).


Round 1 · 0 of this PR's 25 automated comments used · this round is capped at 20 across all reviewers.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Correctness review — 0 finding(s).

Also, on lines this PR does not change:

  • contrib/python/cyber-guardian-agent/app/tools.py:111 — [MAJOR] If asset_rows is empty because the hostname was not found, the subsequent return statement accessing asset_rows[0] will raise an IndexError. Can we check if asset_rows is non-empty before accessing its elements?

Round 1 · 0 of this PR's 25 automated comments used · this round is capped at 20 across all reviewers.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Maintainability review — 2 finding(s).


Round 1 · 0 of this PR's 25 automated comments used · this round is capped at 20 across all reviewers.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Security review — 2 finding(s).


Round 1 · 0 of this PR's 25 automated comments used · this round is capped at 20 across all reviewers.

@github-actions github-actions Bot added the status/not-ready-for-review Review requests held until checks pass and bot comments are resolved label Oct 7, 2026
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

This pull request isn't ready for review yet.

I removed the review request for tommywagz until the items below are fixed.

Failing checks (1)

Fix the failing checks. Once everything is green I will re-request the review automatically, so there is no need to ping anyone. This is re-checked every 15 minutes.

Applied by the review gate (.github/review-gate-config.yml); the status/not-ready-for-review label is removed on release.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Correctness review — 1 finding(s).


Round 2 · 7 of this PR's 25 automated comments used · this round is capped at 4 across all reviewers · after this round only Security and Correctness run.

client.query(context_query, job_config=job_config).result()
)
asset_context = (
dict(asset_rows[0]) if asset_rows else "No asset context found."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MAJOR] It seems the fallback path here returns a string while the success path returns a dictionary. Can we return an empty dictionary instead to keep the JSON schema consistent?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b5e8918 — updated the fallback in triageQueryTool to return {} when asset_rows is empty so asset_context is consistently a dictionary.

@github-actions github-actions Bot removed the status/not-ready-for-review Review requests held until checks pass and bot comments are resolved label Oct 7, 2026

@happyhuman happyhuman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just reviewed the PR and left a few comments. PTAL

* **`network_connection_log`**:
* **Schema:** `log_timestamp, source_host, source_ip, destination_ip, destination_port, protocol, process_pid_ref`
* **Purpose:** Correlate network egress for EDR, confirm `SourceHost` to `DestinationIP` for IOC_MATCH.
* *(Implicit `mail_logs` for PHISHING_EMAIL - assumed schema for sender, recipient, URL, SMTP relay)*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Where does mail_logs come from? I only see six CSVs in sample_data/ and none of them is mail logs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — removed the non-existent mail_logs reference and updated the PHISHING_EMAIL step to analyze extracted email entities and correlate with network_connection_log.

4. **Construct Output and Transfer:**
* Create the JSON object containing the `recommended_actions` list.
* Return the JSON object.
* *(Note: `responseExecutionTool` is used by the Orchestrator *after* HITL approval for actual (simulated) execution, not by the Response Agent directly in this prompting context.)*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This note says responseExecutionTool isn't used by the Response Agent, but it's listed under Tools above. Which one is right?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — removed the contradictory note and updated Step 3 to clarify that response_agent calls responseExecutionTool for auto-approved actions (requires_approval: false) while deferring requires_approval: true actions for HITL approval.

If classification is: IOC-heavy [IOC_MATCH, PHISHING_EMAIL]
Do NOT output intermediate conversational messages or wait for user confirmation before transferring. Perform all agent transfers immediately and autonomously.
Always follow this path "CALL threatintel_agent FIRST AND THEN CALL investigation_agent" with the below instructions
Delegate to Threat Intel (threat_intel_agent): Call threatintel_agent(indicators=list_of_extracted_iocs).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

uses both threat_intel_agent and threatintel_agent here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — standardized all references in app/prompt.py to threat_intel_agent.


**Input:**
* `indicators`: A list of strings, where each string is an IOC value (e.g., IP address, hash, URL) (list[str]).
Note: Always send the input to the tool as a list even if there is only 1 IOC value or Empty string if there are no IOC values.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this be an empty list rather than an empty string? indicators is typed list[str] just above.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — updated the note to specify an empty list ([]) instead of an empty string.

rows = list(client.query(query, job_config=job_config).result())
return bq_rows_to_json(rows)

return "Query did not match a known investigation type. Please provide more specific parameters."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason this branch returns a plain string? The other two return JSON.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — wrapped the fallback error message in json.dumps({"error": ...}) so all branches return JSON.


# --- Configure Logging ---
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

logger isn't used in this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — removed the unused logging import and logger variable.

Step 2: Delegate to Triage (Always First)
Action: Call the triage_agent.
Agent Call: triage_agent(hostname=extracted_hostname, alert_type=classification)
Critical Check: If the triage_agent returns is_duplicate: true, you MUST stop all further processing. Log this result and terminate the workflow.,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

stray comma after workflow.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — removed the stray comma after workflow..

version = "0.1.0"
description = "AI-driven agent for cybersecurity incident response"
authors = [
{ name = "Cyber Guardian Team" },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you please put real names here? Cyber Guardian Team and Google POC look like placeholders.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 036cc4d — updated authors to Abhilash Adavi and Anas Aslam.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Security review — 1 finding(s).


Round 3 · 8 of this PR's 25 automated comments used · this round is capped at 1 across all reviewers · only Security and Correctness still run.

* `action_command`: Value from `ActionCommand`.
* `target`: Value from `TargetDescription`.
* `requires_approval`: Value from `RequiresApproval`.
* If `requires_approval` is `false`, call `responseExecutionTool(action=action_command, target=target)` to simulate immediate execution. If `requires_approval` is `true`, do not execute it until Human-In-The-Loop (HITL) approval is granted.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MAJOR] Is there a reason we are allowing the response agent to autonomously execute actions without Human-In-The-Loop approval when requires_approval is false? This could lead to unauthorized tool execution if the triggering inputs are manipulated.

@github-actions github-actions Bot added the status/not-ready-for-review Review requests held until checks pass and bot comments are resolved label Nov 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/not-ready-for-review Review requests held until checks pass and bot comments are resolved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants