Skip to content

Add parser validation APIs and log-type CLI commands#201

Open
ishree-dev wants to merge 1 commit intogoogle:mainfrom
ishree-dev:cli-changes
Open

Add parser validation APIs and log-type CLI commands#201
ishree-dev wants to merge 1 commit intogoogle:mainfrom
ishree-dev:cli-changes

Conversation

@ishree-dev
Copy link

This PR introduces new SDK wrapper methods and CLI commands for Chronicle Parser Validation tooling. It specifically adds functionality to trigger GitHub checks for parsers and retrieve their corresponding analysis reports.
Key Changes:

  • New SDK Methods (src/secops/chronicle/client.py & parser_validation.py):

  • trigger_github_checks: Triggers a parser analysis report against an associated GitHub PR.

  • get_analysis_report: Retrieves a completed parser analysis report using its full resource name.

  • New CLI Commands (src/secops/cli/commands/log_type.py):

    • Implemented the secops log-type command group.
    • secops log-type trigger-checks --associated-pr --log-type <LOG_TYPE> --customer-id
    • secops log-type get-analysis-report --name <REPORT_NAME>
  • Testing (tests/):

    • Added unit tests for the newly added ChronicleClient methods (test_client_parser_validation.py).
    • Added unit tests for CLI command handlers (test_log_type.py).
    • Added integration tests for the log-type CLI lifecycle (test_log_type_integration.py).
  • Documentation:
    Updated api_module_mapping.md to map logTypes.triggerGitHubChecks and logTypes.getParserAnalysisReport to their respective CLI counterparts.

@ishree-dev ishree-dev requested a review from mihirvala08 as a code owner March 16, 2026 11:32
archived=archived,
run_frequency=run_frequency,
)

Copy link
Member

Choose a reason for hiding this comment

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

Please remove these lines.

Copy link
Author

Choose a reason for hiding this comment

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

resolved

raise SecOpsError(f"No parsers found for log type: {log_type}")

# Use the first parser's name (which includes the UUID)
parser_name = parsers_data["parsers"][0]["name"]

Choose a reason for hiding this comment

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

This may lead to null pointer exception. You're only checking if parsers field is present or not. It may be present and emtpy.

Copy link
Author

Choose a reason for hiding this comment

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

comment resolved

raise SecOpsError(f"No parsers found for log type: {log_type}")

# Use the first parser's name (which includes the UUID)
parser_name = parsers_data["parsers"][0]["name"]

Choose a reason for hiding this comment

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

What's the logic to choose the first parser among multiple?

Copy link
Author

Choose a reason for hiding this comment

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

multiple isnt supported right?

Choose a reason for hiding this comment

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

Are we letting the user know about that any how? We should add a log that multiple parsers are not supported if there are instead of directly choosing the first.

Copy link
Author

Choose a reason for hiding this comment

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

I will add the log then

APIError: If the API request fails.
"""
instance_id = client.instance_id
if customer_id and customer_id != client.customer_id:

Choose a reason for hiding this comment

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

Can we initialize the client itself with the correct customer id if you're anyway getting it in the argument?

Choose a reason for hiding this comment

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

Check if the customer id is already available via the gcloud auth command. In that case no need to provide the customer id separately.

Copy link
Author

Choose a reason for hiding this comment

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

comment resolved

# First get the list of parsers for this log_type to find a valid
# parser UUID
parsers_url = f"{base_url}/{instance_id}/logTypes/{log_type}/parsers"
parsers_resp = client.session.get(parsers_url, timeout=timeout)

Choose a reason for hiding this comment

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

Can't we use the some information from metadata.json file instead of fetching the parsers for this logtype and then using the first available name?

Copy link
Author

Choose a reason for hiding this comment

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

we are expecting log_type from the cli command

Choose a reason for hiding this comment

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

Correct.

)

parsers_data = parsers_resp.json()
if not parsers_data.get("parsers"):

Choose a reason for hiding this comment

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

If no parser exists for that log type, why are we not triggering the github check api?

Copy link
Author

Choose a reason for hiding this comment

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

comment resolved

result = chronicle.get_analysis_report(name=args.name)
output_formatter(result, args.output)
except APIError as e:
print(f"API error: {e}", file=sys.stderr)

Choose a reason for hiding this comment

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

Same comment here.

except APIError as e:
print(f"API error: {e}", file=sys.stderr)
sys.exit(1)
except SecOpsError as e:

Choose a reason for hiding this comment

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

When will this error come?

Copy link
Author

Choose a reason for hiding this comment

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

This SecOpsError is caught because chronicle.get_analysis_report(name=args.name) performs local input validation before sending the request. If the user passes an empty or invalid name
(e.g., less than 5 characters long), the internal client method raises a SecOpsError rather than an APIError.


result = mock_client.trigger_github_checks(
associated_pr="owner/repo/pull/123",
log_type="BRO_DNS",

Choose a reason for hiding this comment

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

Replace this with DUMMY_LOGTYPE

Copy link
Author

Choose a reason for hiding this comment

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

resolved

"""Test successful trigger_checks command execution."""
args = Namespace(
associated_pr="owner/repo/pull/123",
log_type="BRO_DNS",

Choose a reason for hiding this comment

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

Same comment.

Copy link
Author

Choose a reason for hiding this comment

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

resolved

"log-type",
"trigger-checks",
"--associated-pr",
"google/secops-wrapper/pull/1",

Choose a reason for hiding this comment

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

Why choose the 1st PR? Is that related to this project?

Copy link
Author

Choose a reason for hiding this comment

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

this is a sample pr, can be any number

@ishree-dev ishree-dev marked this pull request as draft March 20, 2026 07:35
@ishree-dev ishree-dev marked this pull request as ready for review March 20, 2026 07:35
@ishree-dev ishree-dev force-pushed the cli-changes branch 2 times, most recently from 1172e71 to a119b36 Compare March 23, 2026 08:02
if not isinstance(log_type, str) or len(log_type.strip()) < 2:
raise SecOpsError("log_type must be a valid string of length >= 2")
if customer_id is not None:
if not isinstance(customer_id, str) or len(customer_id.strip()) < 2:

Choose a reason for hiding this comment

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

Lets use pattern matching for Customer Id as its a UUID


def trigger_github_checks(
client: "ChronicleClient",
associated_pr: str,

Choose a reason for hiding this comment

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

Lets add proper validations for the structure of this string, validate by breaking the path and number of substrings, valid pr number.

# First get the list of parsers for this log_type to find a valid
# parser UUID
parsers_url = f"{base_url}/{instance_id}/logTypes/{log_type}/parsers"
parsers_resp = client.session.get(parsers_url, timeout=timeout)

Choose a reason for hiding this comment

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

Correct.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants