From dd4633fabc17072c934d3f656be18f2c9dbccfa0 Mon Sep 17 00:00:00 2001 From: pad Date: Wed, 6 Nov 2024 10:05:59 +0100 Subject: [PATCH] Add format_context and complete output_format for RPC call to format This will help #SAF-1696 test plan: see related PR in semgrep --- semgrep_output_v1.atd | 47 ++++- semgrep_output_v1.jsonschema | 25 ++- semgrep_output_v1.proto | 8 +- semgrep_output_v1.py | 212 +++++++++++++++++++++-- semgrep_output_v1.ts | 78 ++++++++- semgrep_output_v1_j.ml | 325 +++++++++++++++++++++++++++++++++-- semgrep_output_v1_j.mli | 33 +++- 7 files changed, 681 insertions(+), 47 deletions(-) diff --git a/semgrep_output_v1.atd b/semgrep_output_v1.atd index a730d2ab..c0419350 100644 --- a/semgrep_output_v1.atd +++ b/semgrep_output_v1.atd @@ -1756,6 +1756,10 @@ type ci_env = (string * string) list (* See src/rpc/README.txt in the Semgrep repository. *) +(* ----------------------------- *) +(* argument call types *) +(* ----------------------------- *) + (* coupling: Textedit.ml *) type edit = { @@ -1821,11 +1825,32 @@ type sarif_format_return format_time_seconds: float; } -(* TODO: merge with src/osemgrep/reporting/Output_format.ml *) -type output_format = [ - | Vim +type output_format + + = [ + | Text + | Json | Emacs -] + | Vim + | Sarif + | Gitlab_sast + | Gitlab_secrets + | Junit_xml + (* osemgrep-only *) + | Files_with_matches + (* used to disable the final display of match results because + * we displayed them incrementally instead + *) + | Incremental +] + +type format_context + + = { + is_ci_invocation: bool; + is_logged_in: bool; + is_using_registry: bool; +} type manifest_kind @@ -1891,12 +1916,16 @@ type dump_rule_partitions_params = { output_dir: fpath; } +(* ----------------------------- *) +(* The call *) +(* ----------------------------- *) + type function_call = [ | CallContributions | CallApplyFixes of apply_fixes_params | CallSarifFormat of sarif_format_params - | CallFormatter of (output_format * cli_output) + | CallFormatter of (output_format * format_context * cli_output) (* NOTE: fpath is most likely a temporary file that contains all the rules in JSON format. In the future, we could send the rules via a big string through the RPC pipe. @@ -1906,6 +1935,10 @@ type function_call | CallDumpRulePartitions of dump_rule_partitions_params ] +(* ----------------------------- *) +(* The return *) +(* ----------------------------- *) + type function_return = [ | RetError of string @@ -1918,6 +1951,10 @@ type function_return | RetDumpRulePartitions of bool ] +(*****************************************************************************) +(* Misc *) +(*****************************************************************************) + (* ----------------------------- *) (* Partial scans. Experimental and for internal use only. *) (* ----------------------------- *) diff --git a/semgrep_output_v1.jsonschema b/semgrep_output_v1.jsonschema index 966b88c3..057c5d83 100644 --- a/semgrep_output_v1.jsonschema +++ b/semgrep_output_v1.jsonschema @@ -1519,7 +1519,27 @@ } }, "output_format": { - "oneOf": [ { "const": "Vim" }, { "const": "Emacs" } ] + "oneOf": [ + { "const": "Text" }, + { "const": "Json" }, + { "const": "Emacs" }, + { "const": "Vim" }, + { "const": "Sarif" }, + { "const": "Gitlab_sast" }, + { "const": "Gitlab_secrets" }, + { "const": "Junit_xml" }, + { "const": "Files_with_matches" }, + { "const": "Incremental" } + ] + }, + "format_context": { + "type": "object", + "required": [ "is_ci_invocation", "is_logged_in", "is_using_registry" ], + "properties": { + "is_ci_invocation": { "type": "boolean" }, + "is_logged_in": { "type": "boolean" }, + "is_using_registry": { "type": "boolean" } + } }, "manifest_kind": { "oneOf": [ @@ -1648,10 +1668,11 @@ { "const": "CallFormatter" }, { "type": "array", - "minItems": 2, + "minItems": 3, "items": false, "prefixItems": [ { "$ref": "#/definitions/output_format" }, + { "$ref": "#/definitions/format_context" }, { "$ref": "#/definitions/cli_output" } ] } diff --git a/semgrep_output_v1.proto b/semgrep_output_v1.proto index 43821969..5b2ced2d 100644 --- a/semgrep_output_v1.proto +++ b/semgrep_output_v1.proto @@ -1,6 +1,6 @@ // Generated by jsonschema2protobuf. DO NOT EDIT! // Source file: semgrep_output_v1.jsonschema -// Source file sha256 digest: 751a837f0c98b0b88e3280aa46281f4da018fd3da381e5a6df0e7a0a1a8f98c9 +// Source file sha256 digest: a1bfa497a5f60d9ccfc5bff6297bc05ccc7ff1580818dd702b8076f763a7244f syntax = "proto3"; @@ -625,6 +625,12 @@ message SarifFormatReturn { float format_time_seconds = 462877603; } +message FormatContext { + bool is_ci_invocation = 208091716; + bool is_logged_in = 214813956; + bool is_using_registry = 133534925; +} + message Manifest { google.protobuf.Any kind = 3088172; string path = 3212859; diff --git a/semgrep_output_v1.py b/semgrep_output_v1.py index c054fedc..42787d87 100644 --- a/semgrep_output_v1.py +++ b/semgrep_output_v1.py @@ -6525,24 +6525,41 @@ def to_json_string(self, **kw: Any) -> str: return json.dumps(self.to_json(), **kw) -@dataclass -class Vim: - """Original type: output_format = [ ... | Vim | ... ]""" +@dataclass(frozen=True) +class Text: + """Original type: output_format = [ ... | Text | ... ]""" @property def kind(self) -> str: """Name of the class representing this variant.""" - return 'Vim' + return 'Text' @staticmethod def to_json() -> Any: - return 'Vim' + return 'Text' def to_json_string(self, **kw: Any) -> str: return json.dumps(self.to_json(), **kw) -@dataclass +@dataclass(frozen=True) +class Json: + """Original type: output_format = [ ... | Json | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'Json' + + @staticmethod + def to_json() -> Any: + return 'Json' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) class Emacs: """Original type: output_format = [ ... | Emacs | ... ]""" @@ -6559,11 +6576,130 @@ def to_json_string(self, **kw: Any) -> str: return json.dumps(self.to_json(), **kw) -@dataclass +@dataclass(frozen=True) +class Vim: + """Original type: output_format = [ ... | Vim | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'Vim' + + @staticmethod + def to_json() -> Any: + return 'Vim' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class Sarif: + """Original type: output_format = [ ... | Sarif | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'Sarif' + + @staticmethod + def to_json() -> Any: + return 'Sarif' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class GitlabSast: + """Original type: output_format = [ ... | Gitlab_sast | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'GitlabSast' + + @staticmethod + def to_json() -> Any: + return 'Gitlab_sast' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class GitlabSecrets: + """Original type: output_format = [ ... | Gitlab_secrets | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'GitlabSecrets' + + @staticmethod + def to_json() -> Any: + return 'Gitlab_secrets' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class JunitXml: + """Original type: output_format = [ ... | Junit_xml | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'JunitXml' + + @staticmethod + def to_json() -> Any: + return 'Junit_xml' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class FilesWithMatches: + """Original type: output_format = [ ... | Files_with_matches | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'FilesWithMatches' + + @staticmethod + def to_json() -> Any: + return 'Files_with_matches' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) +class Incremental: + """Original type: output_format = [ ... | Incremental | ... ]""" + + @property + def kind(self) -> str: + """Name of the class representing this variant.""" + return 'Incremental' + + @staticmethod + def to_json() -> Any: + return 'Incremental' + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + +@dataclass(frozen=True) class OutputFormat: """Original type: output_format = [ ... ]""" - value: Union[Vim, Emacs] + value: Union[Text, Json, Emacs, Vim, Sarif, GitlabSast, GitlabSecrets, JunitXml, FilesWithMatches, Incremental] @property def kind(self) -> str: @@ -6573,10 +6709,26 @@ def kind(self) -> str: @classmethod def from_json(cls, x: Any) -> 'OutputFormat': if isinstance(x, str): - if x == 'Vim': - return cls(Vim()) + if x == 'Text': + return cls(Text()) + if x == 'Json': + return cls(Json()) if x == 'Emacs': return cls(Emacs()) + if x == 'Vim': + return cls(Vim()) + if x == 'Sarif': + return cls(Sarif()) + if x == 'Gitlab_sast': + return cls(GitlabSast()) + if x == 'Gitlab_secrets': + return cls(GitlabSecrets()) + if x == 'Junit_xml': + return cls(JunitXml()) + if x == 'Files_with_matches': + return cls(FilesWithMatches()) + if x == 'Incremental': + return cls(Incremental()) _atd_bad_json('OutputFormat', x) _atd_bad_json('OutputFormat', x) @@ -7173,6 +7325,40 @@ def to_json_string(self, **kw: Any) -> str: return json.dumps(self.to_json(), **kw) +@dataclass(frozen=True) +class FormatContext: + """Original type: format_context = { ... }""" + + is_ci_invocation: bool + is_logged_in: bool + is_using_registry: bool + + @classmethod + def from_json(cls, x: Any) -> 'FormatContext': + if isinstance(x, dict): + return cls( + is_ci_invocation=_atd_read_bool(x['is_ci_invocation']) if 'is_ci_invocation' in x else _atd_missing_json_field('FormatContext', 'is_ci_invocation'), + is_logged_in=_atd_read_bool(x['is_logged_in']) if 'is_logged_in' in x else _atd_missing_json_field('FormatContext', 'is_logged_in'), + is_using_registry=_atd_read_bool(x['is_using_registry']) if 'is_using_registry' in x else _atd_missing_json_field('FormatContext', 'is_using_registry'), + ) + else: + _atd_bad_json('FormatContext', x) + + def to_json(self) -> Any: + res: Dict[str, Any] = {} + res['is_ci_invocation'] = _atd_write_bool(self.is_ci_invocation) + res['is_logged_in'] = _atd_write_bool(self.is_logged_in) + res['is_using_registry'] = _atd_write_bool(self.is_using_registry) + return res + + @classmethod + def from_json_string(cls, x: str) -> 'FormatContext': + return cls.from_json(json.loads(x)) + + def to_json_string(self, **kw: Any) -> str: + return json.dumps(self.to_json(), **kw) + + @dataclass(frozen=True) class Edit: """Original type: edit = { ... }""" @@ -7393,7 +7579,7 @@ def to_json_string(self, **kw: Any) -> str: class CallFormatter: """Original type: function_call = [ ... | CallFormatter of ... | ... ]""" - value: Tuple[OutputFormat, CliOutput] + value: Tuple[OutputFormat, FormatContext, CliOutput] @property def kind(self) -> str: @@ -7401,7 +7587,7 @@ def kind(self) -> str: return 'CallFormatter' def to_json(self) -> Any: - return ['CallFormatter', (lambda x: [(lambda x: x.to_json())(x[0]), (lambda x: x.to_json())(x[1])] if isinstance(x, tuple) and len(x) == 2 else _atd_bad_python('tuple of length 2', x))(self.value)] + return ['CallFormatter', (lambda x: [(lambda x: x.to_json())(x[0]), (lambda x: x.to_json())(x[1]), (lambda x: x.to_json())(x[2])] if isinstance(x, tuple) and len(x) == 3 else _atd_bad_python('tuple of length 3', x))(self.value)] def to_json_string(self, **kw: Any) -> str: return json.dumps(self.to_json(), **kw) @@ -7485,7 +7671,7 @@ def from_json(cls, x: Any) -> 'FunctionCall': if cons == 'CallSarifFormat': return cls(CallSarifFormat(SarifFormatParams.from_json(x[1]))) if cons == 'CallFormatter': - return cls(CallFormatter((lambda x: (OutputFormat.from_json(x[0]), CliOutput.from_json(x[1])) if isinstance(x, list) and len(x) == 2 else _atd_bad_json('array of length 2', x))(x[1]))) + return cls(CallFormatter((lambda x: (OutputFormat.from_json(x[0]), FormatContext.from_json(x[1]), CliOutput.from_json(x[2])) if isinstance(x, list) and len(x) == 3 else _atd_bad_json('array of length 3', x))(x[1]))) if cons == 'CallValidate': return cls(CallValidate(Fpath.from_json(x[1]))) if cons == 'CallResolveDependencies': diff --git a/semgrep_output_v1.ts b/semgrep_output_v1.ts index 1b32587c..312dff17 100644 --- a/semgrep_output_v1.ts +++ b/semgrep_output_v1.ts @@ -833,8 +833,22 @@ export type SarifFormatReturn = { } export type OutputFormat = -| { kind: 'Vim' } +| { kind: 'Text' } +| { kind: 'Json' } | { kind: 'Emacs' } +| { kind: 'Vim' } +| { kind: 'Sarif' } +| { kind: 'Gitlab_sast' } +| { kind: 'Gitlab_secrets' } +| { kind: 'Junit_xml' } +| { kind: 'Files_with_matches' } +| { kind: 'Incremental' } + +export type FormatContext = { + is_ci_invocation: boolean; + is_logged_in: boolean; + is_using_registry: boolean; +} export type ManifestKind = | { kind: 'RequirementsIn' } @@ -882,7 +896,7 @@ export type FunctionCall = | { kind: 'CallContributions' } | { kind: 'CallApplyFixes'; value: ApplyFixesParams } | { kind: 'CallSarifFormat'; value: SarifFormatParams } -| { kind: 'CallFormatter'; value: [OutputFormat, CliOutput] } +| { kind: 'CallFormatter'; value: [OutputFormat, FormatContext, CliOutput] } | { kind: 'CallValidate'; value: Fpath } | { kind: 'CallResolveDependencies'; value: Manifest[] } | { kind: 'CallDumpRulePartitions'; value: DumpRulePartitionsParams } @@ -3391,25 +3405,73 @@ export function readSarifFormatReturn(x: any, context: any = x): SarifFormatRetu export function writeOutputFormat(x: OutputFormat, context: any = x): any { switch (x.kind) { - case 'Vim': - return 'Vim' + case 'Text': + return 'Text' + case 'Json': + return 'Json' case 'Emacs': return 'Emacs' + case 'Vim': + return 'Vim' + case 'Sarif': + return 'Sarif' + case 'Gitlab_sast': + return 'Gitlab_sast' + case 'Gitlab_secrets': + return 'Gitlab_secrets' + case 'Junit_xml': + return 'Junit_xml' + case 'Files_with_matches': + return 'Files_with_matches' + case 'Incremental': + return 'Incremental' } } export function readOutputFormat(x: any, context: any = x): OutputFormat { switch (x) { - case 'Vim': - return { kind: 'Vim' } + case 'Text': + return { kind: 'Text' } + case 'Json': + return { kind: 'Json' } case 'Emacs': return { kind: 'Emacs' } + case 'Vim': + return { kind: 'Vim' } + case 'Sarif': + return { kind: 'Sarif' } + case 'Gitlab_sast': + return { kind: 'Gitlab_sast' } + case 'Gitlab_secrets': + return { kind: 'Gitlab_secrets' } + case 'Junit_xml': + return { kind: 'Junit_xml' } + case 'Files_with_matches': + return { kind: 'Files_with_matches' } + case 'Incremental': + return { kind: 'Incremental' } default: _atd_bad_json('OutputFormat', x, context) throw new Error('impossible') } } +export function writeFormatContext(x: FormatContext, context: any = x): any { + return { + 'is_ci_invocation': _atd_write_required_field('FormatContext', 'is_ci_invocation', _atd_write_bool, x.is_ci_invocation, x), + 'is_logged_in': _atd_write_required_field('FormatContext', 'is_logged_in', _atd_write_bool, x.is_logged_in, x), + 'is_using_registry': _atd_write_required_field('FormatContext', 'is_using_registry', _atd_write_bool, x.is_using_registry, x), + }; +} + +export function readFormatContext(x: any, context: any = x): FormatContext { + return { + is_ci_invocation: _atd_read_required_field('FormatContext', 'is_ci_invocation', _atd_read_bool, x['is_ci_invocation'], x), + is_logged_in: _atd_read_required_field('FormatContext', 'is_logged_in', _atd_read_bool, x['is_logged_in'], x), + is_using_registry: _atd_read_required_field('FormatContext', 'is_using_registry', _atd_read_bool, x['is_using_registry'], x), + }; +} + export function writeManifestKind(x: ManifestKind, context: any = x): any { switch (x.kind) { case 'RequirementsIn': @@ -3593,7 +3655,7 @@ export function writeFunctionCall(x: FunctionCall, context: any = x): any { case 'CallSarifFormat': return ['CallSarifFormat', writeSarifFormatParams(x.value, x)] case 'CallFormatter': - return ['CallFormatter', ((x, context) => [writeOutputFormat(x[0], x), writeCliOutput(x[1], x)])(x.value, x)] + return ['CallFormatter', ((x, context) => [writeOutputFormat(x[0], x), writeFormatContext(x[1], x), writeCliOutput(x[2], x)])(x.value, x)] case 'CallValidate': return ['CallValidate', writeFpath(x.value, x)] case 'CallResolveDependencies': @@ -3621,7 +3683,7 @@ export function readFunctionCall(x: any, context: any = x): FunctionCall { case 'CallSarifFormat': return { kind: 'CallSarifFormat', value: readSarifFormatParams(x[1], x) } case 'CallFormatter': - return { kind: 'CallFormatter', value: ((x, context): [OutputFormat, CliOutput] => { _atd_check_json_tuple(2, x, context); return [readOutputFormat(x[0], x), readCliOutput(x[1], x)] })(x[1], x) } + return { kind: 'CallFormatter', value: ((x, context): [OutputFormat, FormatContext, CliOutput] => { _atd_check_json_tuple(3, x, context); return [readOutputFormat(x[0], x), readFormatContext(x[1], x), readCliOutput(x[2], x)] })(x[1], x) } case 'CallValidate': return { kind: 'CallValidate', value: readFpath(x[1], x) } case 'CallResolveDependencies': diff --git a/semgrep_output_v1_j.ml b/semgrep_output_v1_j.ml index 8a6fbdc8..5aafdc53 100644 --- a/semgrep_output_v1_j.ml +++ b/semgrep_output_v1_j.ml @@ -657,7 +657,11 @@ type ci_scan_complete = Semgrep_output_v1_t.ci_scan_complete = { type partial_scan_result = Semgrep_output_v1_t.partial_scan_result -type output_format = Semgrep_output_v1_t.output_format +type output_format = Semgrep_output_v1_t.output_format = + Text | Json | Emacs | Vim | Sarif | Gitlab_sast | Gitlab_secrets + | Junit_xml | Files_with_matches | Incremental + + [@@deriving show] type manifest_kind = Semgrep_output_v1_t.manifest_kind [@@deriving show, eq, yojson] @@ -681,6 +685,13 @@ type apply_fixes_return = Semgrep_output_v1_t.apply_fixes_return = { type function_return = Semgrep_output_v1_t.function_return +type format_context = Semgrep_output_v1_t.format_context = { + is_ci_invocation: bool; + is_logged_in: bool; + is_using_registry: bool +} + [@@deriving show] + type edit = Semgrep_output_v1_t.edit = { path: fpath; start_offset: int; @@ -26298,11 +26309,19 @@ let read_partial_scan_result = ( ) let partial_scan_result_of_string s = read_partial_scan_result (Yojson.Safe.init_lexer ()) (Lexing.from_string s) -let write_output_format = ( - fun ob x -> +let write_output_format : _ -> output_format -> _ = ( + fun ob (x : output_format) -> match x with - | `Vim -> Buffer.add_string ob "\"Vim\"" - | `Emacs -> Buffer.add_string ob "\"Emacs\"" + | Text -> Buffer.add_string ob "\"Text\"" + | Json -> Buffer.add_string ob "\"Json\"" + | Emacs -> Buffer.add_string ob "\"Emacs\"" + | Vim -> Buffer.add_string ob "\"Vim\"" + | Sarif -> Buffer.add_string ob "\"Sarif\"" + | Gitlab_sast -> Buffer.add_string ob "\"Gitlab_sast\"" + | Gitlab_secrets -> Buffer.add_string ob "\"Gitlab_secrets\"" + | Junit_xml -> Buffer.add_string ob "\"Junit_xml\"" + | Files_with_matches -> Buffer.add_string ob "\"Files_with_matches\"" + | Incremental -> Buffer.add_string ob "\"Incremental\"" ) let string_of_output_format ?(len = 1024) x = let ob = Buffer.create len in @@ -26314,23 +26333,71 @@ let read_output_format = ( match Yojson.Safe.start_any_variant p lb with | `Edgy_bracket -> ( match Yojson.Safe.read_ident p lb with - | "Vim" -> + | "Text" -> Yojson.Safe.read_space p lb; Yojson.Safe.read_gt p lb; - `Vim + (Text : output_format) + | "Json" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Json : output_format) | "Emacs" -> Yojson.Safe.read_space p lb; Yojson.Safe.read_gt p lb; - `Emacs + (Emacs : output_format) + | "Vim" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Vim : output_format) + | "Sarif" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Sarif : output_format) + | "Gitlab_sast" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Gitlab_sast : output_format) + | "Gitlab_secrets" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Gitlab_secrets : output_format) + | "Junit_xml" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Junit_xml : output_format) + | "Files_with_matches" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Files_with_matches : output_format) + | "Incremental" -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_gt p lb; + (Incremental : output_format) | x -> Atdgen_runtime.Oj_run.invalid_variant_tag p x ) | `Double_quote -> ( match Yojson.Safe.finish_string p lb with - | "Vim" -> - `Vim + | "Text" -> + (Text : output_format) + | "Json" -> + (Json : output_format) | "Emacs" -> - `Emacs + (Emacs : output_format) + | "Vim" -> + (Vim : output_format) + | "Sarif" -> + (Sarif : output_format) + | "Gitlab_sast" -> + (Gitlab_sast : output_format) + | "Gitlab_secrets" -> + (Gitlab_secrets : output_format) + | "Junit_xml" -> + (Junit_xml : output_format) + | "Files_with_matches" -> + (Files_with_matches : output_format) + | "Incremental" -> + (Incremental : output_format) | x -> Atdgen_runtime.Oj_run.invalid_variant_tag p x ) @@ -27403,6 +27470,202 @@ let read_function_return = ( ) let function_return_of_string s = read_function_return (Yojson.Safe.init_lexer ()) (Lexing.from_string s) +let write_format_context : _ -> format_context -> _ = ( + fun ob (x : format_context) -> + Buffer.add_char ob '{'; + let is_first = ref true in + if !is_first then + is_first := false + else + Buffer.add_char ob ','; + Buffer.add_string ob "\"is_ci_invocation\":"; + ( + Yojson.Safe.write_bool + ) + ob x.is_ci_invocation; + if !is_first then + is_first := false + else + Buffer.add_char ob ','; + Buffer.add_string ob "\"is_logged_in\":"; + ( + Yojson.Safe.write_bool + ) + ob x.is_logged_in; + if !is_first then + is_first := false + else + Buffer.add_char ob ','; + Buffer.add_string ob "\"is_using_registry\":"; + ( + Yojson.Safe.write_bool + ) + ob x.is_using_registry; + Buffer.add_char ob '}'; +) +let string_of_format_context ?(len = 1024) x = + let ob = Buffer.create len in + write_format_context ob x; + Buffer.contents ob +let read_format_context = ( + fun p lb -> + Yojson.Safe.read_space p lb; + Yojson.Safe.read_lcurl p lb; + let field_is_ci_invocation = ref (None) in + let field_is_logged_in = ref (None) in + let field_is_using_registry = ref (None) in + try + Yojson.Safe.read_space p lb; + Yojson.Safe.read_object_end lb; + Yojson.Safe.read_space p lb; + let f = + fun s pos len -> + if pos < 0 || len < 0 || pos + len > String.length s then + invalid_arg (Printf.sprintf "out-of-bounds substring position or length: string = %S, requested position = %i, requested length = %i" s pos len); + match len with + | 12 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'l' && String.unsafe_get s (pos+4) = 'o' && String.unsafe_get s (pos+5) = 'g' && String.unsafe_get s (pos+6) = 'g' && String.unsafe_get s (pos+7) = 'e' && String.unsafe_get s (pos+8) = 'd' && String.unsafe_get s (pos+9) = '_' && String.unsafe_get s (pos+10) = 'i' && String.unsafe_get s (pos+11) = 'n' then ( + 1 + ) + else ( + -1 + ) + ) + | 16 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'c' && String.unsafe_get s (pos+4) = 'i' && String.unsafe_get s (pos+5) = '_' && String.unsafe_get s (pos+6) = 'i' && String.unsafe_get s (pos+7) = 'n' && String.unsafe_get s (pos+8) = 'v' && String.unsafe_get s (pos+9) = 'o' && String.unsafe_get s (pos+10) = 'c' && String.unsafe_get s (pos+11) = 'a' && String.unsafe_get s (pos+12) = 't' && String.unsafe_get s (pos+13) = 'i' && String.unsafe_get s (pos+14) = 'o' && String.unsafe_get s (pos+15) = 'n' then ( + 0 + ) + else ( + -1 + ) + ) + | 17 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'u' && String.unsafe_get s (pos+4) = 's' && String.unsafe_get s (pos+5) = 'i' && String.unsafe_get s (pos+6) = 'n' && String.unsafe_get s (pos+7) = 'g' && String.unsafe_get s (pos+8) = '_' && String.unsafe_get s (pos+9) = 'r' && String.unsafe_get s (pos+10) = 'e' && String.unsafe_get s (pos+11) = 'g' && String.unsafe_get s (pos+12) = 'i' && String.unsafe_get s (pos+13) = 's' && String.unsafe_get s (pos+14) = 't' && String.unsafe_get s (pos+15) = 'r' && String.unsafe_get s (pos+16) = 'y' then ( + 2 + ) + else ( + -1 + ) + ) + | _ -> ( + -1 + ) + in + let i = Yojson.Safe.map_ident p f lb in + Atdgen_runtime.Oj_run.read_until_field_value p lb; + ( + match i with + | 0 -> + field_is_ci_invocation := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | 1 -> + field_is_logged_in := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | 2 -> + field_is_using_registry := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | _ -> ( + Yojson.Safe.skip_json p lb + ) + ); + while true do + Yojson.Safe.read_space p lb; + Yojson.Safe.read_object_sep p lb; + Yojson.Safe.read_space p lb; + let f = + fun s pos len -> + if pos < 0 || len < 0 || pos + len > String.length s then + invalid_arg (Printf.sprintf "out-of-bounds substring position or length: string = %S, requested position = %i, requested length = %i" s pos len); + match len with + | 12 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'l' && String.unsafe_get s (pos+4) = 'o' && String.unsafe_get s (pos+5) = 'g' && String.unsafe_get s (pos+6) = 'g' && String.unsafe_get s (pos+7) = 'e' && String.unsafe_get s (pos+8) = 'd' && String.unsafe_get s (pos+9) = '_' && String.unsafe_get s (pos+10) = 'i' && String.unsafe_get s (pos+11) = 'n' then ( + 1 + ) + else ( + -1 + ) + ) + | 16 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'c' && String.unsafe_get s (pos+4) = 'i' && String.unsafe_get s (pos+5) = '_' && String.unsafe_get s (pos+6) = 'i' && String.unsafe_get s (pos+7) = 'n' && String.unsafe_get s (pos+8) = 'v' && String.unsafe_get s (pos+9) = 'o' && String.unsafe_get s (pos+10) = 'c' && String.unsafe_get s (pos+11) = 'a' && String.unsafe_get s (pos+12) = 't' && String.unsafe_get s (pos+13) = 'i' && String.unsafe_get s (pos+14) = 'o' && String.unsafe_get s (pos+15) = 'n' then ( + 0 + ) + else ( + -1 + ) + ) + | 17 -> ( + if String.unsafe_get s pos = 'i' && String.unsafe_get s (pos+1) = 's' && String.unsafe_get s (pos+2) = '_' && String.unsafe_get s (pos+3) = 'u' && String.unsafe_get s (pos+4) = 's' && String.unsafe_get s (pos+5) = 'i' && String.unsafe_get s (pos+6) = 'n' && String.unsafe_get s (pos+7) = 'g' && String.unsafe_get s (pos+8) = '_' && String.unsafe_get s (pos+9) = 'r' && String.unsafe_get s (pos+10) = 'e' && String.unsafe_get s (pos+11) = 'g' && String.unsafe_get s (pos+12) = 'i' && String.unsafe_get s (pos+13) = 's' && String.unsafe_get s (pos+14) = 't' && String.unsafe_get s (pos+15) = 'r' && String.unsafe_get s (pos+16) = 'y' then ( + 2 + ) + else ( + -1 + ) + ) + | _ -> ( + -1 + ) + in + let i = Yojson.Safe.map_ident p f lb in + Atdgen_runtime.Oj_run.read_until_field_value p lb; + ( + match i with + | 0 -> + field_is_ci_invocation := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | 1 -> + field_is_logged_in := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | 2 -> + field_is_using_registry := ( + Some ( + ( + Atdgen_runtime.Oj_run.read_bool + ) p lb + ) + ); + | _ -> ( + Yojson.Safe.skip_json p lb + ) + ); + done; + assert false; + with Yojson.End_of_object -> ( + ( + { + is_ci_invocation = (match !field_is_ci_invocation with Some x -> x | None -> Atdgen_runtime.Oj_run.missing_field p "is_ci_invocation"); + is_logged_in = (match !field_is_logged_in with Some x -> x | None -> Atdgen_runtime.Oj_run.missing_field p "is_logged_in"); + is_using_registry = (match !field_is_using_registry with Some x -> x | None -> Atdgen_runtime.Oj_run.missing_field p "is_using_registry"); + } + : format_context) + ) +) +let format_context_of_string s = + read_format_context (Yojson.Safe.init_lexer ()) (Lexing.from_string s) let write_edit : _ -> edit -> _ = ( fun ob (x : edit) -> Buffer.add_char ob '{'; @@ -28849,13 +29112,19 @@ let write_function_call = ( ( fun ob x -> Buffer.add_char ob '['; - (let x, _ = x in + (let x, _, _ = x in ( write_output_format ) ob x ); Buffer.add_char ob ','; - (let _, x = x in + (let _, x, _ = x in + ( + write_format_context + ) ob x + ); + Buffer.add_char ob ','; + (let _, _, x = x in ( write_cli_output ) ob x @@ -28935,6 +29204,17 @@ let read_function_call = ( x in let x1 = + let x = + ( + read_format_context + ) p lb + in + incr len; + Yojson.Safe.read_space p lb; + Yojson.Safe.read_tuple_sep2 p std_tuple lb; + x + in + let x2 = let x = ( read_cli_output @@ -28956,9 +29236,9 @@ let read_function_call = ( done with Yojson.End_of_tuple -> () ); - (x0, x1) + (x0, x1, x2) with Yojson.End_of_tuple -> - Atdgen_runtime.Oj_run.missing_tuple_fields p !len [ 0; 1 ]); + Atdgen_runtime.Oj_run.missing_tuple_fields p !len [ 0; 1; 2 ]); ) p lb in Yojson.Safe.read_space p lb; @@ -29048,6 +29328,17 @@ let read_function_call = ( x in let x1 = + let x = + ( + read_format_context + ) p lb + in + incr len; + Yojson.Safe.read_space p lb; + Yojson.Safe.read_tuple_sep2 p std_tuple lb; + x + in + let x2 = let x = ( read_cli_output @@ -29069,9 +29360,9 @@ let read_function_call = ( done with Yojson.End_of_tuple -> () ); - (x0, x1) + (x0, x1, x2) with Yojson.End_of_tuple -> - Atdgen_runtime.Oj_run.missing_tuple_fields p !len [ 0; 1 ]); + Atdgen_runtime.Oj_run.missing_tuple_fields p !len [ 0; 1; 2 ]); ) p lb in Yojson.Safe.read_space p lb; diff --git a/semgrep_output_v1_j.mli b/semgrep_output_v1_j.mli index 96b5fd8b..dbfaec75 100644 --- a/semgrep_output_v1_j.mli +++ b/semgrep_output_v1_j.mli @@ -657,7 +657,11 @@ type ci_scan_complete = Semgrep_output_v1_t.ci_scan_complete = { type partial_scan_result = Semgrep_output_v1_t.partial_scan_result -type output_format = Semgrep_output_v1_t.output_format +type output_format = Semgrep_output_v1_t.output_format = + Text | Json | Emacs | Vim | Sarif | Gitlab_sast | Gitlab_secrets + | Junit_xml | Files_with_matches | Incremental + + [@@deriving show] type manifest_kind = Semgrep_output_v1_t.manifest_kind [@@deriving show, eq, yojson] @@ -681,6 +685,13 @@ type apply_fixes_return = Semgrep_output_v1_t.apply_fixes_return = { type function_return = Semgrep_output_v1_t.function_return +type format_context = Semgrep_output_v1_t.format_context = { + is_ci_invocation: bool; + is_logged_in: bool; + is_using_registry: bool +} + [@@deriving show] + type edit = Semgrep_output_v1_t.edit = { path: fpath; start_offset: int; @@ -2975,6 +2986,26 @@ val function_return_of_string : string -> function_return (** Deserialize JSON data of type {!type:function_return}. *) +val write_format_context : + Buffer.t -> format_context -> unit + (** Output a JSON value of type {!type:format_context}. *) + +val string_of_format_context : + ?len:int -> format_context -> string + (** Serialize a value of type {!type:format_context} + into a JSON string. + @param len specifies the initial length + of the buffer used internally. + Default: 1024. *) + +val read_format_context : + Yojson.Safe.lexer_state -> Lexing.lexbuf -> format_context + (** Input JSON data of type {!type:format_context}. *) + +val format_context_of_string : + string -> format_context + (** Deserialize JSON data of type {!type:format_context}. *) + val write_edit : Buffer.t -> edit -> unit (** Output a JSON value of type {!type:edit}. *)