package MlFront_Exec

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file SecDist.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
module DistIo =
  MlFront_Thunk.ThunkDist.Io (BuildCore.Alacarte_xpromise_apparatus.Promise)

module CstIo =
  MlFront_Thunk.ThunkCst.Io (BuildCore.Alacarte_xpromise_apparatus.Promise)

let dist_dir_fp = MlFront_Core.FilePath.of_string_exn "etc/dk/d"

(** [scan_dist_dir module_or] scans the distribution directory for JSON files.

    If any distribution files are found, {b all} of the distribution packages
    must belong to a single package. The package library cannot be
    ["MlFront_Attestation"] or ["MlFront_Std"], or have the vendor ["Our"].
    Confer:
    {!Assumptions.distributions_fetched_for_all_modules_except_Our_vendor_and_mlfront_modules}.

    A pair [pkg_opt, registry] is returned.

    [pkg_opt] will be the single package [Some pkg] contained in the
    distribution directory, or [None] if there were no distribution files.

    [registry] is a package registry containing the single package [pkg], or
    empty if [pkg_opt = None].

    Any error will be printed and the process will be exited. *)
let scan_dist_dir module_or =
  let quick_error msg =
    let lines = String.split_on_char '\n' msg in
    Format.eprintf "@[<v 1>FATAL: Scanning distribution files failed.@;%a@]@."
      Format.(pp_print_list ~pp_sep:pp_print_cut pp_print_string)
      lines;
    exit 1
  in
  let files =
    try
      Sys.readdir (MlFront_Core.FilePath.to_string dist_dir_fp) |> Array.to_list
    with Sys_error _ -> []
  in
  let json_files =
    List.filter (fun f -> Filename.check_suffix f ".dist.json") files
  in
  let read_dist_file dist_file =
    let file = BuildCore.Io.disk_file dist_file in
    match
      BuildCore.Alacarte_xpromise_apparatus.Promise.run_promise
      @@ DistIo.parse_core module_or file
    with
    | Error { error_range = _; error_message; is_rendered = _ } ->
        quick_error error_message
    | Ok dist_core -> dist_core
  in
  let show
      ({ id; producer = _; license = _; continuations = _ } :
        MlFront_Thunk.ThunkDist.DistCore.t) dist_file =
    match id with
    | None ->
        Printf.sprintf "<no distribution core id> in `%s`"
          (MlFront_Core.FilePath.to_string dist_file)
    | Some (_range, lib, ver) ->
        Printf.sprintf "%s@%s in `%s`"
          (MlFront_Core.LibraryId.full_name lib)
          (MlFront_Thunk.ThunkSemver64.to_string ver)
          (MlFront_Core.FilePath.to_string dist_file)
  in
  let registry =
    List.fold_left
      (fun registry fn ->
        let dist_file = MlFront_Core.FilePath.append_exn fn dist_dir_fp in
        let dist_core = read_dist_file dist_file in

        (* error if distribution can't be distributed *)
        Assumptions
        .mlfrontattestation_modules_are_embedded_and_cannot_be_imported ();
        if SecPackageRegistry.package_id_for_distcore dist_core = None then
          quick_error
            (Printf.sprintf
               "Distribution `%s` cannot be distributed. Distributed modules \
                cannot belong to the vendor [`Our`] or be part of the library \
                [`MlFront_Attestation`]."
               (show dist_core dist_file));

        Assumptions.distribution_entry_points_only_trace_stores_and_include_dirs
          ();
        SecPackageRegistry.accept_if_not_present
          ~on_duplicate:(fun _dist_file' msg -> quick_error ("FATAL:" ^ msg))
          dist_core dist_file registry)
      (SecPackageRegistry.create ~show ())
      json_files
  in
  match SecPackageRegistry.get_package_ids registry with
  | [] -> (None, registry)
  | [ pkg ] -> (Some pkg, registry)
  | first :: second :: _ ->
      quick_error
        (Printf.sprintf "More than one distribution package found: %s and %s"
           (MlFront_Core.PackageId.full_name first)
           (MlFront_Core.PackageId.full_name second))

(** Finders of text locations in a distribution file. *)
module Locations = struct
  open struct
    let quick_error msg =
      let lines = String.split_on_char '\n' msg in
      Format.eprintf "@[<v 1>FATAL: Parsing distribution file failed.@;%a@]@."
        Format.(pp_print_list ~pp_sep:pp_print_cut pp_print_string)
        lines;
      exit 1

    let read_dist_file module_or dist_file =
      let file = BuildCore.Io.disk_file dist_file in
      match
        BuildCore.Alacarte_xpromise_apparatus.Promise.run_promise
        @@ CstIo.parse module_or file
      with
      | Error { error_range = _; error_message; is_rendered = _ } ->
          quick_error error_message
      | Ok dist_core -> dist_core
  end

  (** [find_empty_github_slsa_v1_l2_location] finds the text location of the
      ["github_slsa_v1_l2"] attestation, which must be empty and zero-width.

      The JSON structure is like this:
      {[
        "distributions": [
          {
            "build": {
              "attestation": {
                "github_slsa_v1_l2": {},
                "github_slsa_v1_l3": {},
                "openbsd_signify": {}
              }
            }
          }
        ]
      ]}

      An error will be returned if more than one distribution is present.

      An error will be returned if the ["github_slsa_v1_l2"] attestation is not
      empty and zero-width. That is, only ["{}"] is allowed as the value of the
      ["github_slsa_v1_l2"] field. *)
  let find_empty_github_slsa_v1_l2_location module_or dist_file =
    let dist_file_s = MlFront_Core.FilePath.to_string dist_file in
    let dist_file_s_opt = Some dist_file_s in
    let cst = read_dist_file module_or dist_file in
    let contents =
      match cst.contents with
      | Some contents -> contents
      | None ->
          quick_error
            (Printf.sprintf "Distribution file `%s` has no contents" dist_file_s)
    in
    let state =
      MlFront_Thunk.ThunkParsers.Results.State.create_with_source
        ~origin:dist_file_s contents
    in
    match cst with
    | {
     contents = _;
     schema = _;
     schema_version = _;
     forms = _;
     bundles = _;
     distributions = [];
    } ->
        Error "Distribution file has no distributions"
    | {
     contents = _;
     schema = _;
     schema_version = _;
     forms = _;
     bundles = _;
     distributions = (first_range, _) :: (second_range, _) :: _;
    } ->
        let r1 = MlFront_Thunk.ThunkLexers.Ranges.display_range first_range in
        let r2 = MlFront_Thunk.ThunkLexers.Ranges.display_range second_range in
        let rboth = Fmlib_parse.Position.merge r1 r2 in
        Error
          (Format.asprintf
             "Distribution file has two or more distributions at %a. Only one \
              distribution is allowed."
             (MlFront_Thunk.ThunkParsers.Results.pp_range dist_file_s_opt)
             rboth)
    | {
     contents = _;
     schema = _;
     schema_version = _;
     forms = _;
     bundles = _;
     distributions =
       [
         ( dist_range,
           {
             id = _id_range, library, version;
             producer = _;
             license = _;
             continuations = _;
             build = { build_to_sign = _; build_attestation };
           } );
       ];
    } -> begin
        let id =
          Printf.sprintf "%s@%s"
            (MlFront_Core.LibraryId.full_name library)
            (MlFront_Thunk.ThunkSemver64.to_string version)
        in
        match
          MlFront_Thunk.ThunkCommand.InternalUse.parse_library_version module_or
            MlFront_Thunk.ThunkParsers.Results.State.none `DirectDecode None id
        with
        | Error msg ->
            Error
              (Format.asprintf "Distribution file has invalid `id` `%s`: %a" id
                 MlFront_Thunk.ThunkParsers.Results.Semantic.pp msg)
        | Ok (library_id, _ver) -> begin
            match build_attestation with
            | None ->
                Error
                  (MlFront_Thunk.ThunkParsers.Results.single_error
                     ~code:"ed954d30" ~msg:"No build attestation found"
                     ~brief_instruction:
                       "Add the build attestation `\"attestation\": { \
                        \"github_slsa_v1_l2\": {}` }."
                     module_or state dist_range)
            | Some
                {
                  attestation_openbsd_signify = _;
                  attestation_github_slsa_v1_l2 = None;
                  attestation_github_slsa_v1_l3 = _;
                } ->
                Error
                  (MlFront_Thunk.ThunkParsers.Results.single_error
                     ~code:"dd4c56e0"
                     ~msg:"No `github_slsa_v1_l2` attestation found"
                     ~brief_instruction:
                       "Add an empty `\"github_slsa_v1_l2\": {}` attestation."
                     module_or state dist_range)
            | Some
                {
                  attestation_openbsd_signify = _;
                  attestation_github_slsa_v1_l2 =
                    Some (range, { github_slsa_v1_l2_doc = Some _ });
                  attestation_github_slsa_v1_l3 = _;
                } ->
                Error
                  (MlFront_Thunk.ThunkParsers.Results.single_error
                     ~code:"ebfbc1e7"
                     ~msg:
                       "A non-empty `github_slsa_v1_l2` attestation was found"
                     ~brief_instruction:
                       "Empty the `github_slsa_v1_l2` attestation to just \
                        `\"github_slsa_v1_l2\": {}`."
                     module_or state range)
            | Some
                {
                  attestation_openbsd_signify = _;
                  attestation_github_slsa_v1_l2 =
                    Some (range, { github_slsa_v1_l2_doc = None });
                  attestation_github_slsa_v1_l3 = _;
                } ->
                let ((start_, end_) as r) =
                  MlFront_Thunk.ThunkLexers.Ranges.display_range range
                in
                (* We want the two characters `{}` in `"github_slsa_v1_l2": {}` on the same line. *)
                if
                  Fmlib_parse.Position.byte_offset start_ + 2
                  <> Fmlib_parse.Position.byte_offset end_
                  || Fmlib_parse.Position.line start_
                     <> Fmlib_parse.Position.line end_
                  || Fmlib_parse.Position.column start_ + 2
                     <> Fmlib_parse.Position.column end_
                then
                  Error
                    (MlFront_Thunk.ThunkParsers.Results.single_error
                       ~code:"d2274851"
                       ~msg:
                         "A non-zero-width `github_slsa_v1_l2` attestation was \
                          found"
                       ~brief_instruction:
                         "Make the `github_slsa_v1_l2` attestation empty and \
                          zero-width like this: `\"github_slsa_v1_l2\": {}`."
                       module_or state range)
                else Ok (r, library_id, contents)
          end
      end
end

let read_attestation_bundle attestation_bundle =
  let quick_error msg =
    let lines = String.split_on_char '\n' msg in
    Format.eprintf "@[<v 1>FATAL: Parsing attestation bundle failed.@;%a@]@."
      Format.(pp_print_list ~pp_sep:pp_print_cut pp_print_string)
      lines;
    exit 1
  in

  (* read lines from JSONL file *)
  let file = BuildCore.Io.disk_file attestation_bundle in
  let lines =
    match
      BuildCore.Alacarte_xpromise_apparatus.Promise.run_promise
      @@ BuildCore.Io.read_all file
    with
    | `Content s ->
        (* non-empty lines only *)
        String.split_on_char '\n' s |> List.filter (fun s -> s <> "")
    | `Error e -> quick_error e
    | `ExceededSizeLimit n ->
        quick_error
          (Printf.sprintf
             "Attestation bundle file exceeds size limit of %Ld bytes" n)
  in

  (* setup a JSON parser that will parse each line *)
  let parse_jsonl_line s =
    let state =
      MlFront_Thunk.ThunkParsers.Results.State.create_with_source
        ~origin:(BuildCore.Io.file_origin file)
        s
    in
    let module P = MlFront_Thunk.ThunkParsers.JsonParsers.JsonParser in
    let p =
      (* nit: we could read the JSONL lines with positions rather than [None] *)
      P.start state None
    in
    let p = P.run_on_string s p in
    let p = P.put_end p in
    if P.has_succeeded p then
      let final = P.final p in
      (* Printf.eprintf "autofix parsed: %s\n"
      (hint final : string); *)
      Ok final
    else begin
      let semantic_range error =
        MlFront_Thunk.ThunkParsers.Results.Semantic.error_range error
      in
      let semantic_message error =
        MlFront_Thunk.ThunkParsers.Results.Semantic.error_message error
        |> Fmlib_pretty.Print.text
      in
      (* Printf.eprintf "read has_succeeded %b\n" (P.has_succeeded p);
          Printf.eprintf "read has_consumed_end %b\n" (P.has_consumed_end p);
          Printf.eprintf "read has_failed_syntax %b\n" (P.has_failed_syntax p);
          Printf.eprintf "read has_failed_semantic %b\n" (P.has_failed_semantic p); *)

      let module Reporter = Fmlib_parse.Error_reporter.Make (P) in
      let s =
        Reporter.(
          (if P.has_failed_syntax p then make_syntax p
           else make semantic_range semantic_message p)
          |> run_on_string s
          |> Fmlib_pretty.Print.layout 80
          |> Fmlib_pretty.Print.string_of)
      in
      Error s
    end
  in
  let parse_line_result =
    List.fold_left
      (fun acc s ->
        match acc with
        | Error e -> Error e
        | Ok acc ->
        match parse_jsonl_line s with
        | Ok json -> Ok (json :: acc)
        | Error e -> Error e)
      (Ok []) lines
  in
  match parse_line_result with
  | Error e -> quick_error e
  | Ok fmlib_jsons ->
      let yojsons =
        List.map MlFront_Thunk.ThunkParsers.JsonParsers.json_for_fmlib_to_yojson
          fmlib_jsons
      in
      List.rev yojsons