changed CHANGELOG.md
 
@@ -1,5 1,14 @@
1
1
# Changelog
2
2
3
## 3.1.0 (2023-09-24)
4
- Strict mode: Exception messages of _thrown_ exceptions are now redacted by default to avoid data unintentionally leaking into logs.
5
This behaviour change is not considered to be breaking backwards compatibility since source data presented in exeption messages is
6
not considered part of the `CSV` public API.
7
- Option to (un)redact exception messages [contributed in [#122](https://github.com/beatrichartz/csv/pull/124) by [@taylor-redden-papa](https://github.com/taylor-redden-papa)
8
9
## 3.0.5 (2022-12-03)
10
- Exclude dialyzer files from library package [contributed in [#121](https://github.com/beatrichartz/csv/pull/121) by [@milmazz](https://github.com/milmazz)
11
3
12
## 3.0.4 (2022-11-19)
4
13
- Add missing `escape_max_lines` to decode options typespec [closes #120](https://github.com/beatrichartz/csv/issues/120)
changed README.md
 
@@ -6,13 6,13 @@
6
6
7
7
Add
8
8
```elixir
9
- {:csv, "~> 3.0"}
9
{:csv, "~> 3.1"}
10
10
```
11
11
to your deps in `mix.exs` like so:
12
12
13
13
```elixir
14
14
defp deps do
15
- [{:csv, "~> 3.0"}]
15
[{:csv, "~> 3.1"}]
16
16
end
17
17
```
18
18
 
@@ -142,6 142,12 @@ occur, aborting the operation:
142
142
File.stream!("data.csv") |> CSV.decode!
143
143
````
144
144
145
Redact data in exceptions that `decode!` throws to avoid potentially sensitive data showing up in logs:
146
```elixir
147
File.stream!("data.csv") |> CSV.decode!(redact_exception: true)
148
```
149
150
145
151
#### Options
146
152
147
153
For all available options [check the docs on `decode`](https://hexdocs.pm/csv/CSV.html#decode/2)
changed hex_metadata.config
 
@@ -13,4 13,4 @@
13
13
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/beatrichartz/csv">>}]}.
14
14
{<<"name">>,<<"csv">>}.
15
15
{<<"requirements">>,[]}.
16
- {<<"version">>,<<"3.0.5">>}.
16
{<<"version">>,<<"3.1.0">>}.
changed lib/csv.ex
 
@@ -17,26 17,26 @@ defmodule CSV do
17
17
18
18
These are the options:
19
19
20
- * `:separator` – The separator token to use, defaults to `?,`.
20
* `:separator` – The separator token to use, defaults to `?,`.
21
21
Must be a codepoint (syntax: ? (your separator)).
22
- * `:escape_character` – The escape character token to use, defaults to `?"`.
22
* `:escape_character` – The escape character token to use, defaults to `?"`.
23
23
Must be a codepoint (syntax: ? (your escape character)).
24
- * `:escape_max_lines` – The number of lines an escape sequence is allowed
24
* `:escape_max_lines` – The number of lines an escape sequence is allowed
25
25
to span, defaults to 10.
26
- * `:field_transform` – A function with arity 1 that will get called with
26
* `:field_transform` – A function with arity 1 that will get called with
27
27
each field and can apply transformations. Defaults to identity function.
28
28
This function will get called for every field and therefore should return
29
29
quickly.
30
- * `:headers` – When set to `true`, will take the first row of
30
* `:headers` – When set to `true`, will take the first row of
31
31
the csv and use it as header values.
32
32
When set to a list, will use the given list as header values.
33
33
When set to `false` (default), will use no header values.
34
34
When set to anything but `false`, the resulting rows in the matrix will
35
35
be maps instead of lists.
36
- * `:validate_row_length` – When set to `true`, will take the first row of
36
* `:validate_row_length` – When set to `true`, will take the first row of
37
37
the csv or its headers and validate that following rows are of the same
38
38
length. Defaults to `false`.
39
- * `:unescape_formulas` – When set to `true`, will remove formula escaping
39
* `:unescape_formulas` – When set to `true`, will remove formula escaping
40
40
inserted to prevent [CSV Injection](https://owasp.org/www-community/attacks/CSV_Injection).
41
41
42
42
## Examples
 
@@ -181,6 181,10 @@ defmodule CSV do
181
181
length. Will raise an error if validation fails. Defaults to `false`.
182
182
* `:unescape_formulas` – When set to `true`, will remove formula escaping
183
183
inserted to prevent [CSV Injection](https://owasp.org/www-community/attacks/CSV_Injection).
184
* `:unredact_exceptions` – When set to `true`, will show csv data in
185
message output of exceptions thrown. Only use this when using CSV strict
186
mode in environments and situations where there is no concern with
187
exception message data leaking in logs. Defaults to `false`.
184
188
185
189
## Examples
186
190
 
@@ -280,22 284,24 @@ defmodule CSV do
280
284
281
285
"""
282
286
283
- @spec decode!(Enumerable.t(), [decode_options()]) :: Enumerable.t()
287
@spec decode!(Enumerable.t(), [decode_options() | {:unredact_exceptions, boolean()}]) ::
288
Enumerable.t()
284
289
def decode!(stream, options \\ []) do
285
290
stream |> Decoder.decode(options) |> raise_errors!(options)
286
291
end
287
292
288
293
defp raise_errors!(stream, options) do
289
294
escape_max_lines = options |> Keyword.get(:escape_max_lines, @escape_max_lines)
295
unredact_exceptions = options |> Keyword.get(:unredact_exceptions, false)
290
296
291
- stream |> Stream.map(&yield_or_raise!(&1, escape_max_lines))
297
stream |> Stream.map(&yield_or_raise!(&1, escape_max_lines, unredact_exceptions))
292
298
end
293
299
294
- defp yield_or_raise!({:error, mod, args}, _) do
295
- raise mod, args [mode: :strict]
300
defp yield_or_raise!({:error, mod, args}, _, unredact_exceptions) do
301
raise mod, args [mode: :strict, unredact: unredact_exceptions]
296
302
end
297
303
298
- defp yield_or_raise!({:ok, row}, _), do: row
304
defp yield_or_raise!({:ok, row}, _, _), do: row
299
305
300
306
defp inline_errors!(stream, options) do
301
307
escape_max_lines = options |> Keyword.get(:escape_max_lines, @escape_max_lines)
 
@@ -304,7 310,7 @@ defmodule CSV do
304
310
end
305
311
306
312
defp yield_or_inline!({:error, mod, args}, _) do
307
- {:error, mod.exception(args [mode: :normal]).message}
313
{:error, mod.exception(args [mode: :normal, unredact: true]).message}
308
314
end
309
315
310
316
defp yield_or_inline!(value, _), do: value
changed lib/csv/exceptions.ex
 
@@ -1,6 1,6 @@
1
1
defmodule CSV.RowLengthError do
2
2
@moduledoc """
3
- Raised at runtime when the CSV has rows of variable length
3
Raised at runtime when the CSV has rows of variable length
4
4
and `validate_row_length` is set to true.
5
5
"""
6
6
 
@@ -29,7 29,8 @@ defmodule CSV.StrayEscapeCharacterError do
29
29
30
30
def exception(options) do
31
31
line = options |> Keyword.fetch!(:line)
32
- sequence = options |> Keyword.fetch!(:sequence)
32
unredact = options |> Keyword.get(:unredact, false)
33
sequence = options |> Keyword.fetch!(:sequence) |> get_sequence(unredact)
33
34
34
35
message =
35
36
"Stray escape character on line #{line}:" <>
 
@@ -41,6 42,9 @@ defmodule CSV.StrayEscapeCharacterError do
41
42
message: message
42
43
}
43
44
end
45
46
defp get_sequence(_, false), do: "**redacted**"
47
defp get_sequence(sequence, true), do: sequence
44
48
end
45
49
46
50
defmodule CSV.EscapeSequenceError do
 
@@ -54,7 58,11 @@ defmodule CSV.EscapeSequenceError do
54
58
def exception(options) do
55
59
line = options |> Keyword.fetch!(:line)
56
60
stream_halted = options |> Keyword.get(:stream_halted, false)
57
- escape_sequence_start = options |> Keyword.fetch!(:escape_sequence_start)
61
unredact = options |> Keyword.get(:unredact, false)
62
63
escape_sequence_start =
64
options |> Keyword.fetch!(:escape_sequence_start) |> get_sequence(unredact)
65
58
66
mode = options |> Keyword.fetch!(:mode)
59
67
60
68
continues_parsing =
 
@@ -86,4 94,7 @@ defmodule CSV.EscapeSequenceError do
86
94
message: message
87
95
}
88
96
end
97
98
defp get_sequence(_, false), do: "**redacted**"
99
defp get_sequence(sequence, true), do: sequence
89
100
end
changed mix.exs
 
@@ -6,7 6,7 @@ defmodule CSV.Mixfile do
6
6
def project do
7
7
[
8
8
app: :csv,
9
- version: "3.0.5",
9
version: "3.1.0",
10
10
elixir: "~> 1.5",
11
11
deps: deps(),
12
12
package: package(),
 
@@ -46,7 46,7 @@ defmodule CSV.Mixfile do
46
46
47
47
defp deps do
48
48
[
49
- {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false},
49
{:dialyxir, "~> 1.3.0", only: [:dev, :test], runtime: false},
50
50
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
51
51
{:excoveralls, "~> 0.15", only: :test},
52
52
{:benchfella, ">= 0.0.0", only: :bench},