Skip to content

Commit

Permalink
topdown: Fixing nanos int overflow issue for time.* built-in functions (
Browse files Browse the repository at this point in the history
open-policy-agent#4117)

* topdown: Fixing nanos int overflow issue for time.* built-in functions

The go Time.UnixNano() function result is undefined for dates that
cannot fit into an int64 when converted to Unix time in nanoseconds.

Updating built-in functions to return error if date is too low/high.

Fixes: open-policy-agent#4098
Signed-off-by: Johan Fylling <[email protected]>
  • Loading branch information
johanfylling authored Dec 10, 2021
1 parent bbe8afd commit edf5f25
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 366 deletions.
6 changes: 3 additions & 3 deletions docs/content/policy-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -800,13 +800,13 @@ result_valid_hs256 := io.jwt.verify_hs256(result_hs256, "foo")
| Built-in | Description | Wasm Support |
| ------- |-------------|---------------|
| <span class="opa-keep-it-together">``output := time.now_ns()``</span> | ``output`` is a ``number`` representing the current time since epoch in nanoseconds. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.parse_ns(layout, value)``</span> | ``output`` is a ``number`` representing the time ``value`` in nanoseconds since epoch. See the [Go `time` package documentation](https://golang.org/pkg/time/#Parse) for more details on ``layout``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.parse_rfc3339_ns(value)``</span> | ``output`` is a ``number`` representing the time ``value`` in nanoseconds since epoch. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.parse_ns(layout, value)``</span> | ``output`` is a ``number`` representing the time ``value`` in nanoseconds since epoch; or ``undefined`` if outside the valid time range that can fit within an ``int64``. See the [Go `time` package documentation](https://golang.org/pkg/time/#Parse) for more details on ``layout``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.parse_rfc3339_ns(value)``</span> | ``output`` is a ``number`` representing the time ``value`` in nanoseconds since epoch; or ``undefined`` if outside the valid time range that can fit within an ``int64``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.parse_duration_ns(duration)``</span> | ``output`` is a ``number`` representing the duration ``duration`` in nanoseconds. See the [Go `time` package documentation](https://golang.org/pkg/time/#ParseDuration) for more details on ``duration``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.date(ns)``<br/>``output := time.date([ns, tz])``</span> | ``output`` is of the form ``[year, month, day]``, which includes the ``year``, ``month`` (0-12), and ``day`` (0-31) as ``number``s representing the date from the nanoseconds since epoch (``ns``) in the timezone (``tz``), if supplied, or as UTC.| ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.clock(ns)``<br/>``output := time.clock([ns, tz])``</span> | ``output`` is of the form ``[hour, minute, second]``, which outputs the ``hour``, ``minute`` (0-59), and ``second`` (0-59) as ``number``s representing the time of day for the nanoseconds since epoch (``ns``) in the timezone (``tz``), if supplied, or as UTC. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``day := time.weekday(ns)``<br/>``day := time.weekday([ns, tz])``</span> | outputs the ``day`` as ``string`` representing the day of the week for the nanoseconds since epoch (``ns``) in the timezone (``tz``), if supplied, or as UTC. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.add_date(ns, years, months, days)``</span> | ``output`` is a ``number`` representing the time since epoch in nanoseconds after adding the ``years``, ``months`` and ``days`` to ``ns``. See the [Go `time` package documentation](https://golang.org/pkg/time/#Time.AddDate) for more details on ``add_date``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.add_date(ns, years, months, days)``</span> | ``output`` is a ``number`` representing the time since epoch in nanoseconds after adding the ``years``, ``months`` and ``days`` to ``ns``; or ``undefined`` if outside the valid time range that can fit within an ``int64``. See the [Go `time` package documentation](https://golang.org/pkg/time/#Time.AddDate) for more details on ``add_date``. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := time.diff(ns1, ns2)``<br/>``output := time.diff([ns1, tz1], [ns2, tz2])``</span> | ``output`` is of the form ``[year(s), month(s), day(s), hour(s), minute(s), second(s)]``, which outputs ``year(s)``, ``month(s)`` (0-11), ``day(s)`` (0-30), ``hour(s)``(0-23), ``minute(s)``(0-59) and ``second(s)``(0-59) as ``number``s representing the difference between the the two timestamps in nanoseconds since epoch (``ns1`` and ``ns2``), in the timezones (``tz1`` and ``tz2``, respectively), if supplied, or as UTC. | ``SDK-dependent`` |

> Multiple calls to the `time.now_ns` built-in function within a single policy
Expand Down
131 changes: 43 additions & 88 deletions test/cases/testdata/time/test-time-0948.yaml
Original file line number Diff line number Diff line change
@@ -1,95 +1,50 @@
cases:
- data:
a:
- 1
- 2
- 3
- 4
b:
v1: hello
v2: goodbye
c:
- x:
- true
- false
- foo
"y":
- null
- 3.14159
z:
p: true
q: false
d:
e:
- bar
- baz
f:
- xs:
- 1
ys:
- 2
- xs:
- 2
ys:
- 3
g:
a:
- 1
- 0
- 0
- 0
b:
- 0
- 2
- 0
- 0
c:
- 0
- 0
- 0
- 4
h:
- - 1
- 2
- 3
- - 2
- 3
- 4
l:
- a: bob
b: -1
c:
- 1
- 2
- 3
- 4
- a: alice
b: 1
c:
- 2
- 3
- 4
- 5
d: null
m: []
numbers:
- "1"
- "2"
- "3"
- "4"
strings:
bar: 2
baz: 3
foo: 1
three: 3
- note: time/parse_nanos
modules:
- |
package generated
p = ns {
time.parse_ns("2006-01-02T15:04:05Z07:00", "2017-06-02T19:00:00-07:00", ns)
p[case_id] = ns {
case := input.cases[case_id]
time.parse_ns(case.layout, case.value, ns)
}
note: time/parse nanos
query: data.generated.p = x
input: { cases: {
1: { layout: "2006-01-02T15:04:05Z07:00", value: "2017-06-02T19:00:00-07:00" }, # RFC3339
2: { layout: "2006-01-02T15:04:05Z07:00", value: "1677-09-21T00:12:43.145224192-00:00" }, # Earliest valid time
3: { layout: "2006-01-02T15:04:05Z07:00", value: "2262-04-11T23:47:16.854775807-00:00" }, # Latest valid time
4: { layout: "01/02 03:04:05PM '06 -0700", value: "06/02 07:00:00PM '17 -0700" }, # Layout
5: { layout: "02 Jan 06 15:04 -0700", value: "02 Jun 17 19:00 -0700" }, # RFC822Z
}}
want_result:
- x: 1496455200000000000
- x: {
1: 1496455200000000000,
2: -9223372036854775808,
3: 9223372036854775807,
4: 1496455200000000000,
5: 1496455200000000000
}
- note: time/parse_nanos_too_small
modules:
- |
package generated
p = ns {
time.parse_ns("2006-01-02T15:04:05Z07:00", "1677-09-21T00:12:43.145224191-00:00", ns)
}
query: data.generated.p = x
strict_error: true
want_error_code: eval_builtin_error
want_error: 'time outside of valid range'
- note: time/parse_nanos_too_large
modules:
- |
package generated
p = ns {
time.parse_ns("2006-01-02T15:04:05Z07:00", "2262-04-11T23:47:16.854775808-00:00", ns)
}
query: data.generated.p = x
strict_error: true
want_error_code: eval_builtin_error
want_error: 'time outside of valid range'
135 changes: 44 additions & 91 deletions test/cases/testdata/time/test-time-0949.yaml
Original file line number Diff line number Diff line change
@@ -1,95 +1,48 @@
cases:
- data:
a:
- 1
- 2
- 3
- 4
b:
v1: hello
v2: goodbye
c:
- x:
- true
- false
- foo
"y":
- null
- 3.14159
z:
p: true
q: false
d:
e:
- bar
- baz
f:
- xs:
- 1
ys:
- 2
- xs:
- 2
ys:
- 3
g:
a:
- 1
- 0
- 0
- 0
b:
- 0
- 2
- 0
- 0
c:
- 0
- 0
- 0
- 4
h:
- - 1
- 2
- 3
- - 2
- 3
- 4
l:
- a: bob
b: -1
c:
- 1
- 2
- 3
- 4
- a: alice
b: 1
c:
- 2
- 3
- 4
- 5
d: null
m: []
numbers:
- "1"
- "2"
- "3"
- "4"
strings:
bar: 2
baz: 3
foo: 1
three: 3
- note: time/parse_rfc3339_nanos
modules:
- |
package generated
p = ns {
time.parse_rfc3339_ns("2017-06-02T19:00:00-07:00", ns)
}
note: time/parse rfc3339 nanos
- |
package generated
p[t] = ns {
t = input.cases[_]
time.parse_rfc3339_ns(t, ns)
}
query: data.generated.p = x
input: { cases: [
"1677-09-21T00:12:43.145224192-00:00", # Earliest valid time
"1970-01-01T00:00:00-00:00",
"2017-06-02T19:00:00-07:00",
"2262-04-11T23:47:16.854775807-00:00" # Latest valid time
]}
want_result:
- x: 1496455200000000000
- x: {
"1677-09-21T00:12:43.145224192-00:00": -9223372036854775808,
"1970-01-01T00:00:00-00:00": 0,
"2017-06-02T19:00:00-07:00": 1496455200000000000,
"2262-04-11T23:47:16.854775807-00:00": 9223372036854775807,
}
- note: time/parse_rfc3339_nanos_too_small
modules:
- |
package generated
p = ns {
time.parse_rfc3339_ns("1677-09-21T00:12:43.145224191-00:00", ns)
}
query: data.generated.p = x
strict_error: true
want_error_code: eval_builtin_error
want_error: 'time outside of valid range'
- note: time/parse_rfc3339_nanos_too_large
modules:
- |
package generated
p = ns {
time.parse_rfc3339_ns("2262-04-11T23:47:16.854775808-00:00", ns)
}
query: data.generated.p = x
strict_error: true
want_error_code: eval_builtin_error
want_error: 'time outside of valid range'
Loading

0 comments on commit edf5f25

Please sign in to comment.