Source file secret.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
type kind =
| Singleline
| Multiline
type t = {
kind : kind;
text : string;
comments : string option;
}
let kind_to_string k =
match k with
| Singleline -> "single-line"
| Multiline -> "multi-line"
let singleline_from_text_description text description =
let text = String.trim text in
match description with
| "" -> text
| _ -> Printf.sprintf "%s\n\n%s" text description
let multiline_from_text_description text description =
let text = String.trim text in
let description = String.trim description in
match description with
| "" -> Printf.sprintf "\n\n%s" text
| _ -> Printf.sprintf "\n%s\n\n%s" description text
let format_explainer =
{|
# Secrets and comments formats:
# (multi-line comments _should not_ have empty lines in them)
#
# Single line secret with commments format:
# secret one line
# <empty line>
# comments until end of file
#
# Single line secret without commments format:
# secret one line
#
# Multiline secret with comments format:
# <empty line>
# possibly several lines of comments
# <empty line>
# secret until end of file
#
# Multiline secret without comments format:
# <empty line>
# <empty line>
# secret until end of file
|}
module Validation = struct
type validation_error =
| SingleLineLegacy
| MultilineEmptySecret
| EmptySecret
| InvalidFormat
let validate plaintext =
if String.trim plaintext = "" then Error ("empty secrets are not allowed", EmptySecret)
else (
let lines = String.split_on_char '\n' plaintext in
match lines with
| "" :: :: rest when String.trim comment <> "" ->
let secret, _is_secret =
List.fold_left
(fun (secret, is_secret) line ->
match is_secret, String.trim line with
| true, s when s <> "" -> line :: secret, true
| true, _ -> secret, true
| false, "" -> secret, true
| false, _ -> secret, false)
([], false) rest
in
if secret = [] then Error ("multiline: empty secret", MultilineEmptySecret) else Ok Multiline
| "" :: "" :: secret :: _ when String.trim secret <> "" -> Ok Multiline
| secret :: "" :: when String.trim secret <> "" ->
let has_empty_lines_in_cmts =
match comments with
| [] -> false
| cmts ->
String.concat "\n" cmts |> String.trim |> String.split_on_char '\n' |> List.map String.trim |> List.mem ""
in
(match has_empty_lines_in_cmts with
| true -> Error ("empty lines are not allowed in comments", InvalidFormat)
| false -> Ok Singleline)
| secret :: :: _ when String.trim secret <> "" && String.trim comment <> "" ->
Error
( "single-line secrets with comments should have an empty line between the secret and the comments.",
SingleLineLegacy )
| [ secret ] when String.trim secret <> "" -> Ok Singleline
| _ -> Error ("invalid format", InvalidFormat))
(**
Multiline secret with comments format:
<empty line>
possibly several lines of comments without empty lines
<empty line>
secret until end of file
Multiline secret without comments format:
<empty line>
<empty line>
secret until end of file
Single line secret with commments format:
secret one line
<empty line>
comments until end of file
Single line secret without commments format:
secret one line
Single line secret with commments legacy format [DEPRECATED]:
secret one line
comments until the end of file
*)
let parse_exn plaintext_content =
if String.trim plaintext_content = "" then failwith "empty secrets are not allowed";
let lines = String.split_on_char '\n' plaintext_content in
let =
match comments with
| [] -> None
| _ -> Some (comments |> String.concat "\n" |> String.trim)
in
match lines with
| "" :: tl ->
let text, , (_ : bool) =
List.fold_left
(fun (secret_text, , is_secret) line ->
match is_secret, line with
| true, _ -> line :: secret_text, comments, true
| false, "" -> secret_text, comments, true
| false, s -> secret_text, s :: comments, false)
([], [], false) tl
in
let text = List.rev text |> String.concat "\n" |> String.trim in
if text = "" then failwith "broken format multi-line secret (empty secret text). Please fix secret."
else { kind = Multiline; text; comments = to_comments_format (List.rev comments_lines) }
| text :: "" :: | text :: ->
{ kind = Singleline; text; comments = to_comments_format comments_lines }
| [] -> failwith "empty secrets are not allowed"
let validity_to_string name secret_text =
match validate secret_text with
| Ok kind -> Printf.sprintf "✅ %s [ valid %s ]" name (kind_to_string kind)
| Error (e, _typ) -> Printf.sprintf "❌ %s Invalid format: %s" name e
end