Skip to content

Commit

Permalink
feat(regex): added regex class
Browse files Browse the repository at this point in the history
  • Loading branch information
a1994sc committed Feb 16, 2025
1 parent 8d59626 commit 06ee1fd
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 5 deletions.
7 changes: 3 additions & 4 deletions src/pkg/packager/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import (
)

var imageCheck = regexp.MustCompile(`(?mi)"image":"((([a-z0-9._-]+)/)?([a-z0-9._-]+)(:([a-z0-9._-]+))?)"`)
var imagePodCheck = regexp.MustCompile(`(?mi)^(([a-z0-9._-]+)/)?([a-z0-9._-]+)(:([a-z0-9._-]+))?$`)
var imageFuzzyCheck = regexp.MustCompile(`(?mi)["|=]([a-z0-9\-.\/:]+:[\w.\-]*[a-z\.\-][\w.\-]*)"`)

// FindImages iterates over a Zarf.yaml and attempts to parse any images.
Expand Down Expand Up @@ -456,17 +455,17 @@ func findWhyResources(resources []*unstructured.Unstructured, whyImage, componen

func appendToImageMap(imgMap map[string]bool, pod corev1.PodSpec) map[string]bool {
for _, container := range pod.InitContainers {
if imagePodCheck.MatchString(container.Image) {
if ReferenceRegexp.MatchString(container.Image) {
imgMap[container.Image] = true
}
}
for _, container := range pod.Containers {
if imagePodCheck.MatchString(container.Image) {
if ReferenceRegexp.MatchString(container.Image) {
imgMap[container.Image] = true
}
}
for _, container := range pod.EphemeralContainers {
if imagePodCheck.MatchString(container.Image) {
if ReferenceRegexp.MatchString(container.Image) {
imgMap[container.Image] = true
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/pkg/packager/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,19 @@ func TestFindImages(t *testing.T) {
CreateOpts: types.ZarfCreateOptions{
BaseDir: "./testdata/find-images/valid-image-uri",
},
FindImagesOpts: types.ZarfFindImagesOptions{
SkipCosign: true,
},
},
expectedImages: map[string][]string{
"baseline": {
"ghcr.io/zarf-dev/zarf/agent:v0.38.1",
"ghcr.io/zarf-dev/zarf/agent:sha256-f8b1c2f99349516ae1bd0711a19697abcc41555076b0ae90f1a70ca6b50dcbd8.sig",
"10.0.0.1:443/zarf-dev/zarf/agent:v0.38.1",
"alpine",
"xn--7o8h.com/myimage:9.8.7",
"registry.io/foo/project--id.module--name.ver---sion--name",
"foo_bar:latest",
"foo.com:8080/bar:1.2.3",
},
},
},
Expand Down
101 changes: 101 additions & 0 deletions src/pkg/packager/regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package packager

// Borrow from "github.com/containers/image" with love <3
// https://github.com/containers/image/blob/aa915b75e867d14f6cb486a4fcc7d7c91cf4ca0a/docker/reference/regexp.go

import (
"regexp"
"strings"
)

const (
// alphaNumeric defines the alpha numeric atom, typically a
// component of names. This only allows lower case characters and digits.
alphaNumeric = `[a-z0-9]+`

// separator defines the separators allowed to be embedded in name
// components. This allow one period, one or two underscore and multiple
// dashes. Repeated dashes and underscores are intentionally treated
// differently. In order to support valid hostnames as name components,
// supporting repeated dash was added. Additionally double underscore is
// now allowed as a separator to loosen the restriction for previously
// supported names.
separator = `(?:[._]|__|[-]*)`

// repository name to start with a component as defined by DomainRegexp
// and followed by an optional port.
domainComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`

// The string counterpart for TagRegexp.
tag = `[\w][\w.-]{0,127}`

// The string counterpart for DigestRegexp.
digestPat = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
)

var (
// nameComponent restricts registry path component names to start
// with at least one letter or number, with following parts able to be
// separated by one period, one or two underscore and multiple dashes.
nameComponent = expression(
alphaNumeric,
optional(repeated(separator, alphaNumeric)))

domain = expression(
domainComponent,
optional(repeated(literal(`.`), domainComponent)),
optional(literal(`:`), `[0-9]+`))

namePat = expression(
optional(domain, literal(`/`)),
nameComponent,
optional(repeated(literal(`/`), nameComponent)))

referencePat = anchored(capture(namePat),
optional(literal(":"), capture(tag)),
optional(literal("@"), capture(digestPat)))

ReferenceRegexp = re(referencePat)
)

// re compiles the string to a regular expression.
var re = regexp.MustCompile

// literal compiles s into a literal regular expression, escaping any regexp
// reserved characters.
func literal(s string) string {
return regexp.QuoteMeta(s)
}

// expression defines a full expression, where each regular expression must
// follow the previous.
func expression(res ...string) string {
return strings.Join(res, "")
}

// optional wraps the expression in a non-capturing group and makes the
// production optional.
func optional(res ...string) string {
return group(expression(res...)) + `?`
}

// repeated wraps the regexp in a non-capturing group to get one or more
// matches.
func repeated(res ...string) string {
return group(expression(res...)) + `+`
}

// group wraps the regexp in a non-capturing group.
func group(res ...string) string {
return `(?:` + expression(res...) + `)`
}

// capture wraps the expression in a capturing group.
func capture(res ...string) string {
return `(` + expression(res...) + `)`
}

// anchored anchors the regular expression by adding start and end delimiters.
func anchored(res ...string) string {
return `^` + expression(res...) + `$`
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,29 @@ spec:
app: agent
spec:
containers:
# these should be detected
- name: agent
image: ghcr.io/zarf-dev/zarf/agent:v0.38.1
- name: port
image: 10.0.0.1:443/zarf-dev/zarf/agent:v0.38.1
- name: alpine
image: alpine
- name: punycode
image: xn--7o8h.com/myimage:9.8.7
- name: project
image: registry.io/foo/project--id.module--name.ver---sion--name
- name: seperate
image: foo_bar:latest
- name: domain-port
image: foo.com:8080/bar:1.2.3
# these should NOT be detected
- name: under
image: _docker/_docker
- name: quad-under
image: ____/____
- name: dash-namespace
image: foo/-bar
- name: slash-tag
image: foo.com:http/bar
- name: bad-image
image: registry1.dso.mil*

0 comments on commit 06ee1fd

Please sign in to comment.