Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .semgrep-version
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Tracking Semgrep version is v1.164.0.
Tracking Semgrep version is v1.165.0.

This is necessary to track in order for the oss-release workflow to
know which synced commit to tag as a new release. This is
Expand Down
32 changes: 32 additions & 0 deletions semgrep_metrics.atd
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type payload = {
value: value;
extension: extension;
mcp: mcp;
guardian: guardian;
}

(*****************************************************************************)
Expand Down Expand Up @@ -305,6 +306,37 @@ type mcp = {
?oauth_email <ocaml mutable>: string option;
}

(* This is only used by the external Guardian plugin (e.g. the Claude Code
* hook), not by the Semgrep CLI. It mirrors the mcp section above for the
* fields that apply, plus a few hook-specific ones (hook/blocking).
*)
type guardian = {
(* lifecycle *)
?hook <ocaml mutable>: string option; (* PreToolUse | PostToolUse | ... *)
?session_id <ocaml mutable>: string option;
?login_method <ocaml mutable>: string option; (* oauth | app_token *)
?scanner_version <ocaml mutable>: string option; (* semgrep version from the scan response *)
(* identity (no email/name in v1) *)
?deployment_name <ocaml mutable>: string option;
?deployment_id <ocaml mutable>: int option;
?organization_id <ocaml mutable>: int option;
?oauth_id <ocaml mutable>: string option;
(* tool context being scanned *)
?tool_name <ocaml mutable>: string option; (* Write | Edit | Bash *)
?package_manager <ocaml mutable>: string option; (* npm | poetry | cargo ... *)
?attached_lockfile <ocaml mutable>: string option;
(* scan size *)
?num_scanned_files <ocaml mutable>: int option;
?num_lines <ocaml mutable>: int option;
(* findings *)
?num_findings <ocaml mutable>: int option;
?findings <ocaml mutable>: (string (* rule_id *) * finding) list <json repr="object"> option;
(* outcome *)
?blocking <ocaml mutable>: bool option;
?exit_code <ocaml mutable>: int option;
?errors <ocaml mutable>: string list option;
}

(*****************************************************************************)
(* TODO Response by metrics.semgrep.dev *)
(*****************************************************************************)
Expand Down
101 changes: 101 additions & 0 deletions semgrep_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,104 @@ def to_json_string(self, **kw: Any) -> str:
return json.dumps(self.to_json(), **kw)


@dataclass
class Guardian:
"""Original type: guardian = { ... }
"""

hook: Optional[str] = None
session_id: Optional[str] = None
login_method: Optional[str] = None
scanner_version: Optional[str] = None
deployment_name: Optional[str] = None
deployment_id: Optional[int] = None
organization_id: Optional[int] = None
oauth_id: Optional[str] = None
tool_name: Optional[str] = None
package_manager: Optional[str] = None
attached_lockfile: Optional[str] = None
num_scanned_files: Optional[int] = None
num_lines: Optional[int] = None
num_findings: Optional[int] = None
findings: Optional[List[Tuple[str, Finding]]] = None
blocking: Optional[bool] = None
exit_code: Optional[int] = None
errors: Optional[List[str]] = None

@classmethod
def from_json(cls, x: Any) -> 'Guardian':
if isinstance(x, dict):
return cls(
hook=_atd_read_string(x['hook']) if 'hook' in x else None,
session_id=_atd_read_string(x['session_id']) if 'session_id' in x else None,
login_method=_atd_read_string(x['login_method']) if 'login_method' in x else None,
scanner_version=_atd_read_string(x['scanner_version']) if 'scanner_version' in x else None,
deployment_name=_atd_read_string(x['deployment_name']) if 'deployment_name' in x else None,
deployment_id=_atd_read_int(x['deployment_id']) if 'deployment_id' in x else None,
organization_id=_atd_read_int(x['organization_id']) if 'organization_id' in x else None,
oauth_id=_atd_read_string(x['oauth_id']) if 'oauth_id' in x else None,
tool_name=_atd_read_string(x['tool_name']) if 'tool_name' in x else None,
package_manager=_atd_read_string(x['package_manager']) if 'package_manager' in x else None,
attached_lockfile=_atd_read_string(x['attached_lockfile']) if 'attached_lockfile' in x else None,
num_scanned_files=_atd_read_int(x['num_scanned_files']) if 'num_scanned_files' in x else None,
num_lines=_atd_read_int(x['num_lines']) if 'num_lines' in x else None,
num_findings=_atd_read_int(x['num_findings']) if 'num_findings' in x else None,
findings=_atd_read_assoc_object_into_list(Finding.from_json)(x['findings']) if 'findings' in x else None,
blocking=_atd_read_bool(x['blocking']) if 'blocking' in x else None,
exit_code=_atd_read_int(x['exit_code']) if 'exit_code' in x else None,
errors=_atd_read_list(_atd_read_string)(x['errors']) if 'errors' in x else None,
)
else:
_atd_bad_json('Guardian', x)

def to_json(self) -> Any:
res: Dict[str, Any] = {}
if self.hook is not None:
res['hook'] = _atd_write_string(self.hook)
if self.session_id is not None:
res['session_id'] = _atd_write_string(self.session_id)
if self.login_method is not None:
res['login_method'] = _atd_write_string(self.login_method)
if self.scanner_version is not None:
res['scanner_version'] = _atd_write_string(self.scanner_version)
if self.deployment_name is not None:
res['deployment_name'] = _atd_write_string(self.deployment_name)
if self.deployment_id is not None:
res['deployment_id'] = _atd_write_int(self.deployment_id)
if self.organization_id is not None:
res['organization_id'] = _atd_write_int(self.organization_id)
if self.oauth_id is not None:
res['oauth_id'] = _atd_write_string(self.oauth_id)
if self.tool_name is not None:
res['tool_name'] = _atd_write_string(self.tool_name)
if self.package_manager is not None:
res['package_manager'] = _atd_write_string(self.package_manager)
if self.attached_lockfile is not None:
res['attached_lockfile'] = _atd_write_string(self.attached_lockfile)
if self.num_scanned_files is not None:
res['num_scanned_files'] = _atd_write_int(self.num_scanned_files)
if self.num_lines is not None:
res['num_lines'] = _atd_write_int(self.num_lines)
if self.num_findings is not None:
res['num_findings'] = _atd_write_int(self.num_findings)
if self.findings is not None:
res['findings'] = _atd_write_assoc_list_to_object((lambda x: x.to_json()))(self.findings)
if self.blocking is not None:
res['blocking'] = _atd_write_bool(self.blocking)
if self.exit_code is not None:
res['exit_code'] = _atd_write_int(self.exit_code)
if self.errors is not None:
res['errors'] = _atd_write_list(_atd_write_string)(self.errors)
return res

@classmethod
def from_json_string(cls, x: str) -> 'Guardian':
return cls.from_json(json.loads(x))

def to_json_string(self, **kw: Any) -> str:
return json.dumps(self.to_json(), **kw)


@dataclass
class Extension:
"""Original type: extension = { ... }
Expand Down Expand Up @@ -1248,6 +1346,7 @@ class Payload:
value: Value
extension: Extension
mcp: Mcp
guardian: Guardian
parse_rate: List[Tuple[str, ParseStat]] = field(default_factory=lambda: [])

@classmethod
Expand All @@ -1264,6 +1363,7 @@ def from_json(cls, x: Any) -> 'Payload':
value=Value.from_json(x['value']) if 'value' in x else _atd_missing_json_field('Payload', 'value'),
extension=Extension.from_json(x['extension']) if 'extension' in x else _atd_missing_json_field('Payload', 'extension'),
mcp=Mcp.from_json(x['mcp']) if 'mcp' in x else _atd_missing_json_field('Payload', 'mcp'),
guardian=Guardian.from_json(x['guardian']) if 'guardian' in x else _atd_missing_json_field('Payload', 'guardian'),
parse_rate=_atd_read_assoc_object_into_list(ParseStat.from_json)(x['parse_rate']) if 'parse_rate' in x else [],
)
else:
Expand All @@ -1281,6 +1381,7 @@ def to_json(self) -> Any:
res['value'] = (lambda x: x.to_json())(self.value)
res['extension'] = (lambda x: x.to_json())(self.extension)
res['mcp'] = (lambda x: x.to_json())(self.mcp)
res['guardian'] = (lambda x: x.to_json())(self.guardian)
res['parse_rate'] = _atd_write_assoc_list_to_object((lambda x: x.to_json()))(self.parse_rate)
return res

Expand Down
Loading