Skip to content

Commit

Permalink
tags as versions for acceptable task rules
Browse files Browse the repository at this point in the history
This contains two major changes.

One is that the logic in `lib.bundles`
is written to in the form prose (or close enough) to allow for easier
readings of the logic. Instead of the notion of a task collection and
equality of task bundle references, the code now considers business
logic terms, like "is acceptable", "is out of date", "is expired" and
"newer version exists".

And the second is that tags provided in acceptable task bundle data are
now considered in rules. If tags are provided in acceptable task bundle
they further refine the set of records from acceptable task bundle data
that are examined, i.e. different versions do not interfere with each
other.

For task bundle references tags are computed from the acceptable task
bundle data records by matching against the digest.

reference: EC-223
  • Loading branch information
zregvart committed Nov 21, 2023
1 parent 64d5be1 commit fbe2368
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 95 deletions.
210 changes: 164 additions & 46 deletions policy/lib/bundles.rego
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import data.lib.image
import data.lib.refs
import data.lib.time as time_lib

# Return the bundle reference as is
bundle(task) := refs.task_ref(task).bundle

# Returns a subset of tasks that do not use a bundle reference.
disallowed_task_reference(tasks) := {task |
some task in tasks
not refs.task_ref(task).bundle
not bundle(task)
}

# Returns a subset of tasks that use an empty bundle reference.
Expand All @@ -25,73 +28,188 @@ unpinned_task_bundle(tasks) := {task |
ref.digest == ""
}

# Returns if the required task-bundles data is missing
default missing_task_bundles_data := false

missing_task_bundles_data {
count(data["task-bundles"]) == 0
}

# Returns a subset of tasks that use an acceptable bundle reference, but
# an updated bundle reference exists.
out_of_date_task_bundle(tasks) := {task |
some task in tasks
ref := image.parse(bundle(task))
collection := _collection(ref)

some match_index, out_of_date in collection
is_equal(out_of_date, ref)
match_index > 0
ref := image.parse(_bundle_ref(task, data["task-bundles"]))

_is_out_of_date(ref)
}

# Returns a subset of tasks that do not use an acceptable bundle reference.
unacceptable_task_bundle(tasks) := {task |
some task in tasks
ref := image.parse(bundle(task))
collection := _collection(ref)

matches := [record |
some record in collection
is_equal(record, ref)
]
ref := image.parse(_bundle_ref(task, data["task-bundles"]))

count(matches) == 0
_is_unacceptable(ref)
}

# Returns if the required task-bundles data is missing
default missing_task_bundles_data := false
_is_unacceptable(ref) {
not _record_exists(ref)
}

missing_task_bundles_data {
count(data["task-bundles"]) == 0
_is_unacceptable(ref) {
_newer_version_exists(ref, data["task-bundles"])
_is_expired(ref, data["task-bundles"])
}

# Returns true if the provided bundle reference is acceptable
is_acceptable(bundle_ref) {
ref := image.parse(bundle_ref)
collection := _collection(ref)
matches := [r |
some r in collection
is_equal(r, ref)
]
# Returns true if the provided bundle reference is recorded within the
# acceptable bundles data
_record_exists(ref) {
# all records in acceptable task bundles for the given repository
records := data["task-bundles"][ref.repo]

some record in records

count(matches) > 0
# an acceptable task bundle reference is one that is recorded in the
# acceptable task bundles, this is done by matching it's digest; note no
# care is given to the expiry or freshness
record.digest == ref.digest
}

# Returns whether or not the ref matches the digest of the record.
is_equal(record, ref) := match {
ref.digest != ""
match := record.digest == ref.digest
# Out of date references are those that are acceptable, meaning that their
# reference is recorded in acceptable task bundles data and no newer version
# exists or the reference has expired
_is_out_of_date(ref) {
_record_exists(ref)
_newer_version_exists(ref, data["task-bundles"])
not _is_expired(ref, data["task-bundles"])
}

# Returns whether or not the ref matches the tag of the record as a fallback
# in case the digest is blank for the ref. This is a weaker comparison as,
# unlike digests, tags are not immutable entities. It is expected that a
# missing digest results in a warning whenever possible.
is_equal(record, ref) := match {
ref.digest == ""
match := record.tag == ref.tag
_is_out_of_date(ref) {
_record_exists(ref)
not _newer_version_exists(ref, data["task-bundles"])
_is_expired(ref, data["task-bundles"])
}

bundle(task) := refs.task_ref(task).bundle
# Evaluates to true if the tasks bundle reference is found in the acceptable
# task bundles data, matched by digest, and it is not in effect, meaning that
# it's effective_on is in the past, i.e. it has expired
_is_expired(ref, acceptable) {
# all records in acceptable task bundles for the given repository
records := acceptable[ref.repo]

some record in records

# consider all records, if a match is found via exact digest and it's
# effective_on is greater than current time in effect (remember it is
# configurable) we deem the bundle reference out of date
record.digest == ref.digest

time_lib.effective_current_time_ns > time.parse_rfc3339_ns(record.effective_on)
}

# _collection returns an array representing the full list of records to
# be taken into consideration when evaluating policy rules for bundle
# references. Any irrelevant records are filtered out from the array.
# (The else condition is for when data["task-bundles"][ref.repo] doesn't exist.)
_collection(ref) := items {
full_collection := data["task-bundles"][ref.repo]
items := time_lib.acceptable_items(full_collection)
} else := []
# Evaluates to true if the tasks bundle reference is found in the acceptable
# task bundles data, but also in the data there is a newer version of the task,
# i.e. has a effective_on that is newer than the provided reference's
# effective_on; two references are considered belonging to the same version if
# they have the same tag
_newer_version_exists(ref, acceptable) {
# all records in acceptable task bundles for the given repository
records := acceptable[ref.repo]

some record in records

# consider all records, if a match is found via exact digest and there
# exists a newer record for the same tag but it is newer, i.e. has greater
# effective_on value
record.digest == ref.digest

some other in records

record.tag == other.tag

time.parse_rfc3339_ns(other.effective_on) > time.parse_rfc3339_ns(record.effective_on)
}

# Evaluates to true if the tasks bundle reference is found in the acceptable
# task bundles data, but also there are no records in acceptable task bundles
# data with the same tag and at least one record is newer, i.e. has a
# effective_on that is newer than the provided reference's effective_on. In this
# case we cannot rely on the tags to signal versions so we take all records for
# a specific reference to belong to the same version.
_newer_version_exists(ref, acceptable) {
# all records in acceptable task bundles for the given repository
records := acceptable[ref.repo]

some record in records

# consider all records, if a match is found via exact digest and there
# exists a newer record for the same tag but it is newer, i.e. has greater
# effective_on value
record.digest == ref.digest

# No other record in acceptable bundles matches the tag from the record
# matched by the digest to the reference
count([other |
some other in records
record.digest != other.digest # not the same record
record.tag == other.tag # we found at least one other tag equal to the one we want to compare with
]) == 0

# There are newer records
count([newer |
some newer in records
time.parse_rfc3339_ns(newer.effective_on) > time.parse_rfc3339_ns(record.effective_on)
]) > 0
}

# Determine the image reference of the task bundle, if the provided task bundle
# image reference doesn't have the tag within it try to lookup the tag from the
# acceptable task bundles data
_bundle_ref(task, _) := ref {
ref := bundle(task)
img := image.parse(ref)
img.tag != ""
}

_bundle_ref(task, acceptable) := ref {
ref_no_tag := bundle(task)
img := image.parse(ref_no_tag)
img.tag == ""

# try to find the tag for the reference based on it's digest
records := acceptable[img.repo]

some record in records
record.digest == img.digest
record.tag != ""

ref := image.str({
"digest": img.digest,
"repo": img.repo,
"tag": record.tag,
})
}

_bundle_ref(task, acceptable) := ref {
ref_no_tag := bundle(task)
img := image.parse(ref_no_tag)
img.tag == ""

records := acceptable[img.repo]

count([r | some r in records; r.digest == img.digest]) == 0

ref := ref_no_tag
}

_bundle_ref(task, acceptable) := ref {
ref_no_tag := bundle(task)
img := image.parse(ref_no_tag)
img.tag == ""

not acceptable[img.repo]

ref := ref_no_tag
}
Loading

0 comments on commit fbe2368

Please sign in to comment.