1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: Add external_url_check custom JMESPath function (#8614)

Signed-off-by: lichanghao.orange <lichanghao.orange@bytedance.com>
Signed-off-by: UgOrange <lichanghao.orange@bytedance.com>
This commit is contained in:
UgOrange 2023-11-21 12:17:26 +08:00 committed by GitHub
parent 2902411f50
commit 0079ca1e39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 0 deletions

View file

@ -12,6 +12,8 @@ import (
"errors"
"fmt"
"math"
"net"
"net/url"
"path/filepath"
"reflect"
"regexp"
@ -73,6 +75,7 @@ var (
random = "random"
x509_decode = "x509_decode"
imageNormalize = "image_normalize"
isExternalURL = "is_external_url"
)
func GetFunctions(configuration config.Configuration) []FunctionEntry {
@ -580,6 +583,16 @@ func GetFunctions(configuration config.Configuration) []FunctionEntry {
},
ReturnType: []jpType{jpString},
Note: "normalizes an image reference",
}, {
FunctionEntry: gojmespath.FunctionEntry{
Name: isExternalURL,
Arguments: []argSpec{
{Types: []jpType{jpString}},
},
Handler: jpIsExternalURL,
},
ReturnType: []jpType{jpBool},
Note: "determine if a URL points to an external network address",
}}
}
@ -1221,3 +1234,30 @@ func jpImageNormalize(configuration config.Configuration) gojmespath.JpFunction
}
}
}
func jpIsExternalURL(arguments []interface{}) (interface{}, error) {
var err error
str, err := validateArg(pathCanonicalize, arguments, 0, reflect.String)
if err != nil {
return nil, err
}
parsedURL, err := url.Parse(str.String())
if err != nil {
return false, err
}
ip := net.ParseIP(parsedURL.Hostname())
if ip != nil {
return !(ip.IsLoopback() || ip.IsPrivate()), nil
}
// If it can't be parsed as an IP, then resolve the domain name
ips, err := net.LookupIP(parsedURL.Hostname())
if err != nil {
return nil, err
}
for _, ip := range ips {
if ip.IsLoopback() || ip.IsPrivate() {
return false, nil
}
}
return true, nil
}

View file

@ -1675,3 +1675,61 @@ func Test_ImageNormalize(t *testing.T) {
})
}
}
func Test_IsExternalURL(t *testing.T) {
testCases := []struct {
jmesPath string
expectedResult bool
}{
{
jmesPath: "is_external_url('http://localhost')",
expectedResult: false,
}, {
jmesPath: "is_external_url('http://127.0.0.1')",
expectedResult: false,
}, {
jmesPath: "is_external_url('http://172.16.1.1')",
expectedResult: false,
}, {
jmesPath: "is_external_url('http://10.1.1.1')",
expectedResult: false,
}, {
jmesPath: "is_external_url('http://192.168.0.1')",
expectedResult: false,
}, {
jmesPath: "is_external_url('http://google.com')",
expectedResult: true,
}, {
jmesPath: "is_external_url('http://8.8.8.8')",
expectedResult: true,
}, {
jmesPath: "is_external_url('http://245.48.83.160')",
expectedResult: true,
}, {
jmesPath: "is_external_url('http://[fd12:3456:789a:1::1]')", //192.168.3.6
expectedResult: false,
}, {
jmesPath: "is_external_url('http://[fdb7:d8fd:c4e4:5561::1]')", //182.61.200.7
expectedResult: false,
}, {
jmesPath: "is_external_url('http://[::1]')", //ipv6 loopback
expectedResult: false,
}, {
jmesPath: "is_external_url('http://[2001:db8::ff00:42:8329]')",
expectedResult: true,
}, {
jmesPath: "is_external_url('http://www.google.com@192.168.1.3')", //url with basic auth
expectedResult: false,
},
}
for _, tc := range testCases {
t.Run(tc.jmesPath, func(t *testing.T) {
jp, err := newJMESPath(cfg, tc.jmesPath)
assert.NilError(t, err)
result, err := jp.Search("")
assert.NilError(t, err)
res, ok := result.(bool)
assert.Assert(t, ok)
assert.Equal(t, res, tc.expectedResult)
})
}
}