mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-05 15:37:19 +00:00
Signed-off-by: ayu-ch <ayu.chandekar@gmail.com> Co-authored-by: shuting <shuting@nirmata.com>
1345 lines
37 KiB
Go
1345 lines
37 KiB
Go
package jmespath
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5" // #nosec G501
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1" // #nosec G505
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"net/url"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
trunc "github.com/aquilax/truncate"
|
|
"github.com/blang/semver/v4"
|
|
gojmespath "github.com/kyverno/go-jmespath"
|
|
"github.com/kyverno/kyverno/ext/wildcard"
|
|
"github.com/kyverno/kyverno/pkg/config"
|
|
imageutils "github.com/kyverno/kyverno/pkg/utils/image"
|
|
regen "github.com/zach-klippenstein/goregen"
|
|
"golang.org/x/crypto/cryptobyte"
|
|
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
type PublicKey struct {
|
|
N string
|
|
E int
|
|
}
|
|
|
|
// function names
|
|
var (
|
|
compare = "compare"
|
|
equalFold = "equal_fold"
|
|
replace = "replace"
|
|
replaceAll = "replace_all"
|
|
toUpper = "to_upper"
|
|
toLower = "to_lower"
|
|
trim = "trim"
|
|
trimPrefix = "trim_prefix"
|
|
split = "split"
|
|
regexReplaceAll = "regex_replace_all"
|
|
regexReplaceAllLiteral = "regex_replace_all_literal"
|
|
regexMatch = "regex_match"
|
|
patternMatch = "pattern_match"
|
|
labelMatch = "label_match"
|
|
toBoolean = "to_boolean"
|
|
add = "add"
|
|
sum = "sum"
|
|
subtract = "subtract"
|
|
multiply = "multiply"
|
|
divide = "divide"
|
|
modulo = "modulo"
|
|
round = "round"
|
|
base64Decode = "base64_decode"
|
|
base64Encode = "base64_encode"
|
|
pathCanonicalize = "path_canonicalize"
|
|
truncate = "truncate"
|
|
semverCompare = "semver_compare"
|
|
parseJson = "parse_json"
|
|
parseYAML = "parse_yaml"
|
|
lookup = "lookup"
|
|
items = "items"
|
|
objectFromLists = "object_from_lists"
|
|
random = "random"
|
|
x509_decode = "x509_decode"
|
|
imageNormalize = "image_normalize"
|
|
isExternalURL = "is_external_url"
|
|
SHA256 = "sha256"
|
|
SHA1 = "sha1"
|
|
MD5 = "md5"
|
|
)
|
|
|
|
func GetFunctions(configuration config.Configuration) []FunctionEntry {
|
|
return []FunctionEntry{{
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: compare,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfCompare,
|
|
},
|
|
ReturnType: []jpType{jpNumber},
|
|
Note: "compares two strings lexicographically",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: equalFold,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfEqualFold,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "allows comparing two strings for equivalency where the only differences are letter cases",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: replace,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpNumber}},
|
|
},
|
|
Handler: jpfReplace,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "replaces a specified number of instances of the source string with the replacement string in a parent ",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: replaceAll,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfReplaceAll,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "replace all instances of one string with another in an overall parent string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: toUpper,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfToUpper,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "takes in a string and outputs the same string with all upper-case letters",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: toLower,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfToLower,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "takes in a string and outputs the same string with all lower-case letters",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: trim,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfTrim,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "trims both ends of the source string by characters appearing in the second string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: trimPrefix,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfTrimPrefix,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "trims the second string prefix from the first string if the first string starts with the prefix",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: split,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpfSplit,
|
|
},
|
|
ReturnType: []jpType{jpArrayString},
|
|
Note: "splits the first string when the second string is found and converts it into an array ",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: regexReplaceAll,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
},
|
|
Handler: jpRegexReplaceAll,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "converts all parameters to string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: regexReplaceAllLiteral,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
},
|
|
Handler: jpRegexReplaceAllLiteral,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "converts all parameters to string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: regexMatch,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
},
|
|
Handler: jpRegexMatch,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "first string is the regular exression which is compared with second input which can be a number or string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: patternMatch,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
},
|
|
Handler: jpPatternMatch,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "'*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character",
|
|
}, {
|
|
// Validates if label (param1) would match pod/host/etc labels (param2)
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: labelMatch,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpObject}},
|
|
{Types: []jpType{jpObject}},
|
|
},
|
|
Handler: jpLabelMatch,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: toBoolean,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpToBoolean,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "It returns true or false for any string, such as 'True', 'TruE', 'False', 'FAlse', 'faLSE', etc.",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: add,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpAny}},
|
|
{Types: []jpType{jpAny}},
|
|
},
|
|
Handler: jpAdd,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "does arithmetic addition of two specified values of numbers, quantities, and durations",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: sum,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpArray}},
|
|
},
|
|
Handler: jpSum,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "does arithmetic addition of specified array of values of numbers, quantities, and durations",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: subtract,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpAny}},
|
|
{Types: []jpType{jpAny}},
|
|
},
|
|
Handler: jpSubtract,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "does arithmetic subtraction of two specified values of numbers, quantities, and durations",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: multiply,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpAny}},
|
|
{Types: []jpType{jpAny}},
|
|
},
|
|
Handler: jpMultiply,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "does arithmetic multiplication of two specified values of numbers, quantities, and durations",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: divide,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpAny}},
|
|
{Types: []jpType{jpAny}},
|
|
},
|
|
Handler: jpDivide,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "divisor must be non zero",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: modulo,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpAny}},
|
|
{Types: []jpType{jpAny}},
|
|
},
|
|
Handler: jpModulo,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "divisor must be non-zero, arguments must be integers",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: round,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpNumber}},
|
|
{Types: []jpType{jpNumber}},
|
|
},
|
|
Handler: jpRound,
|
|
},
|
|
ReturnType: []jpType{jpNumber},
|
|
Note: "does roundoff to upto the given decimal places",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: base64Decode,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpBase64Decode,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "decodes a base 64 string",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: base64Encode,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpBase64Encode,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "encodes a regular, plaintext and unencoded string to base64",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeSince,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeSince,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "calculate the difference between a start and end period of time where the end may either be a static definition or the then-current time",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeNow,
|
|
Handler: jpTimeNow,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "returns current time in RFC 3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeNowUtc,
|
|
Handler: jpTimeNowUtc,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "returns current UTC time in RFC 3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: pathCanonicalize,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpPathCanonicalize,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "normalizes or canonicalizes a given path by removing excess slashes",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: truncate,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpNumber}},
|
|
},
|
|
Handler: jpTruncate,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "length argument must be enclosed in backticks; ex. \"{{request.object.metadata.name | truncate(@, `9`)}}\"",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: semverCompare,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpSemverCompare,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "compares two strings which comply with the semantic versioning schema and outputs a boolean response as to the position of the second relative to the first",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: parseJson,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpParseJson,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: parseYAML,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpParseYAML,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "decodes a valid YAML encoded string to the appropriate type provided it can be represented as JSON",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: lookup,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpObject, jpArray}},
|
|
{Types: []jpType{jpString, jpNumber}},
|
|
},
|
|
Handler: jpLookup,
|
|
},
|
|
ReturnType: []jpType{jpAny},
|
|
Note: "returns the value corresponding to the given key/index in the given object/array",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: items,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpObject, jpArray}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpItems,
|
|
},
|
|
ReturnType: []jpType{jpArray},
|
|
Note: "converts a map or array to an array of objects where each key:value is an item in the array",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: objectFromLists,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpArray}},
|
|
{Types: []jpType{jpArray}},
|
|
},
|
|
Handler: jpObjectFromLists,
|
|
},
|
|
ReturnType: []jpType{jpObject},
|
|
Note: "converts a pair of lists containing keys and values to an object",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: random,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpRandom,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "Generates a random sequence of characters",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: x509_decode,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpX509Decode,
|
|
},
|
|
ReturnType: []jpType{jpObject},
|
|
Note: "decodes an x.509 certificate to an object. you may also use this in conjunction with `base64_decode` jmespath function to decode a base64-encoded certificate",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeToCron,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeToCron,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "converts a time (RFC 3339) to a cron expression (string).",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeAdd,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeAdd,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "adds duration (second string) to a time value (first string)",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeParse,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeParse,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "changes a time value of a given layout to RFC 3339",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeUtc,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeUtc,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "calcutes time in UTC from a given time in RFC 3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeDiff,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeDiff,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "calculate the difference between a start and end date in RFC3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeBefore,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeBefore,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "checks if a time is before another time, both in RFC3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeAfter,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeAfter,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "checks if a time is after another time, both in RFC3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeBetween,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeBetween,
|
|
},
|
|
ReturnType: []jpType{jpBool},
|
|
Note: "checks if a time is between a start and end time, all in RFC3339 format",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: timeTruncate,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpTimeTruncate,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "returns the result of rounding time down to a multiple of duration",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: imageNormalize,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpImageNormalize(configuration),
|
|
},
|
|
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",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: SHA256,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpSha256,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "generate unique resources name if length exceeds the limit",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: SHA1,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpSha1,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "generate a SHA-1 hash",
|
|
}, {
|
|
FunctionEntry: gojmespath.FunctionEntry{
|
|
Name: MD5,
|
|
Arguments: []argSpec{
|
|
{Types: []jpType{jpString}},
|
|
},
|
|
Handler: jpMd5,
|
|
},
|
|
ReturnType: []jpType{jpString},
|
|
Note: "generates an MD5 hash",
|
|
}}
|
|
}
|
|
|
|
func jpfCompare(arguments []interface{}) (interface{}, error) {
|
|
if a, err := validateArg(compare, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if b, err := validateArg(compare, arguments, 1, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.Compare(a.String(), b.String()), nil
|
|
}
|
|
}
|
|
|
|
func jpfEqualFold(arguments []interface{}) (interface{}, error) {
|
|
if a, err := validateArg(equalFold, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if b, err := validateArg(equalFold, arguments, 1, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.EqualFold(a.String(), b.String()), nil
|
|
}
|
|
}
|
|
|
|
func jpfReplace(arguments []interface{}) (interface{}, error) {
|
|
if str, err := validateArg(replace, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if old, err := validateArg(replace, arguments, 1, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if new, err := validateArg(replace, arguments, 2, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if n, err := validateArg(replace, arguments, 3, reflect.Float64); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.Replace(str.String(), old.String(), new.String(), int(n.Float())), nil
|
|
}
|
|
}
|
|
|
|
func jpfReplaceAll(arguments []interface{}) (interface{}, error) {
|
|
if str, err := validateArg(replaceAll, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if old, err := validateArg(replaceAll, arguments, 1, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if new, err := validateArg(replaceAll, arguments, 2, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.ReplaceAll(str.String(), old.String(), new.String()), nil
|
|
}
|
|
}
|
|
|
|
func jpfToUpper(arguments []interface{}) (interface{}, error) {
|
|
if str, err := validateArg(toUpper, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.ToUpper(str.String()), nil
|
|
}
|
|
}
|
|
|
|
func jpfToLower(arguments []interface{}) (interface{}, error) {
|
|
if str, err := validateArg(toLower, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return strings.ToLower(str.String()), nil
|
|
}
|
|
}
|
|
|
|
func jpfTrim(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(trim, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cutset, err := validateArg(trim, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.Trim(str.String(), cutset.String()), nil
|
|
}
|
|
|
|
func jpfTrimPrefix(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(trimPrefix, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prefix, err := validateArg(trimPrefix, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.TrimPrefix(str.String(), prefix.String()), nil
|
|
}
|
|
|
|
func jpfSplit(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(split, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sep, err := validateArg(split, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
split := strings.Split(str.String(), sep.String())
|
|
arr := make([]interface{}, len(split))
|
|
|
|
for i, v := range split {
|
|
arr[i] = v
|
|
}
|
|
|
|
return arr, nil
|
|
}
|
|
|
|
func jpRegexReplaceAll(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexReplaceAll, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexReplaceAll, 2, "String or Real")
|
|
}
|
|
|
|
repl, err := ifaceToString(arguments[2])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexReplaceAll, 3, "String or Real")
|
|
}
|
|
|
|
reg, err := regexp.Compile(regex.String())
|
|
if err != nil {
|
|
return nil, formatError(genericError, regexReplaceAll, err.Error())
|
|
}
|
|
return string(reg.ReplaceAll([]byte(src), []byte(repl))), nil
|
|
}
|
|
|
|
func jpRegexReplaceAllLiteral(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexReplaceAllLiteral, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexReplaceAllLiteral, 2, "String or Real")
|
|
}
|
|
|
|
repl, err := ifaceToString(arguments[2])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexReplaceAllLiteral, 3, "String or Real")
|
|
}
|
|
|
|
reg, err := regexp.Compile(regex.String())
|
|
if err != nil {
|
|
return nil, formatError(genericError, regexReplaceAllLiteral, err.Error())
|
|
}
|
|
return string(reg.ReplaceAllLiteral([]byte(src), []byte(repl))), nil
|
|
}
|
|
|
|
func jpRegexMatch(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexMatch, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexMatch, 2, "String or Real")
|
|
}
|
|
|
|
return regexp.Match(regex.String(), []byte(src))
|
|
}
|
|
|
|
func jpPatternMatch(arguments []interface{}) (interface{}, error) {
|
|
pattern, err := validateArg(regexMatch, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, regexMatch, 2, "String or Real")
|
|
}
|
|
|
|
return wildcard.Match(pattern.String(), src), nil
|
|
}
|
|
|
|
func jpLabelMatch(arguments []interface{}) (interface{}, error) {
|
|
labelMap, ok := arguments[0].(map[string]interface{})
|
|
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, labelMatch, 0, "Object")
|
|
}
|
|
|
|
matchMap, ok := arguments[1].(map[string]interface{})
|
|
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, labelMatch, 1, "Object")
|
|
}
|
|
|
|
for key, value := range labelMap {
|
|
if val, ok := matchMap[key]; !ok || val != value {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func jpToBoolean(arguments []interface{}) (interface{}, error) {
|
|
if input, err := validateArg(toBoolean, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else {
|
|
switch strings.ToLower(input.String()) {
|
|
case "true":
|
|
return true, nil
|
|
case "false":
|
|
return false, nil
|
|
default:
|
|
return nil, formatError(genericError, toBoolean, fmt.Sprintf("lowercase argument must be 'true' or 'false' (provided: '%s')", input.String()))
|
|
}
|
|
}
|
|
}
|
|
|
|
func _jpAdd(arguments []interface{}, operator string) (interface{}, error) {
|
|
op1, op2, err := parseArithemticOperands(arguments, operator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return op1.Add(op2, operator)
|
|
}
|
|
|
|
func jpAdd(arguments []interface{}) (interface{}, error) {
|
|
return _jpAdd(arguments, add)
|
|
}
|
|
|
|
func jpSum(arguments []interface{}) (interface{}, error) {
|
|
items, ok := arguments[0].([]interface{})
|
|
if !ok {
|
|
return nil, formatError(typeMismatchError, sum)
|
|
}
|
|
if len(items) == 0 {
|
|
return nil, formatError(genericError, sum, "at least one element in the array is required")
|
|
}
|
|
var err error
|
|
result := items[0]
|
|
for _, item := range items[1:] {
|
|
result, err = _jpAdd([]interface{}{result, item}, sum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func jpSubtract(arguments []interface{}) (interface{}, error) {
|
|
op1, op2, err := parseArithemticOperands(arguments, subtract)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return op1.Subtract(op2)
|
|
}
|
|
|
|
func jpMultiply(arguments []interface{}) (interface{}, error) {
|
|
op1, op2, err := parseArithemticOperands(arguments, multiply)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return op1.Multiply(op2)
|
|
}
|
|
|
|
func jpDivide(arguments []interface{}) (interface{}, error) {
|
|
op1, op2, err := parseArithemticOperands(arguments, divide)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return op1.Divide(op2)
|
|
}
|
|
|
|
func jpModulo(arguments []interface{}) (interface{}, error) {
|
|
op1, op2, err := parseArithemticOperands(arguments, modulo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return op1.Modulo(op2)
|
|
}
|
|
|
|
func jpRound(arguments []interface{}) (interface{}, error) {
|
|
op, err := validateArg(round, arguments, 0, reflect.Float64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
length, err := validateArg(round, arguments, 1, reflect.Float64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
intLength, err := intNumber(length.Float())
|
|
if err != nil {
|
|
return nil, formatError(nonIntRoundError, round)
|
|
}
|
|
if intLength < 0 {
|
|
return nil, formatError(argOutOfBoundsError, round)
|
|
}
|
|
shift := math.Pow(10, float64(intLength))
|
|
rounded := math.Round(op.Float()*shift) / shift
|
|
return rounded, nil
|
|
}
|
|
|
|
func jpBase64Decode(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg("", arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
decodedStr, err := base64.StdEncoding.DecodeString(str.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return string(decodedStr), nil
|
|
}
|
|
|
|
func jpBase64Encode(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg("", arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return base64.StdEncoding.EncodeToString([]byte(str.String())), nil
|
|
}
|
|
|
|
func jpPathCanonicalize(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(pathCanonicalize, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return filepath.Join(str.String()), nil
|
|
}
|
|
|
|
func jpTruncate(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
var normalizedLength float64
|
|
str, err := validateArg(truncate, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
length, err := validateArg(truncate, arguments, 1, reflect.Float64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if length.Float() < 0 {
|
|
normalizedLength = float64(0)
|
|
} else {
|
|
normalizedLength = length.Float()
|
|
}
|
|
|
|
return trunc.Truncator(str.String(), int(normalizedLength), trunc.CutStrategy{}), nil
|
|
}
|
|
|
|
func jpSemverCompare(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
v, err := validateArg(semverCompare, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r, err := validateArg(semverCompare, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
version, _ := semver.Parse(v.String())
|
|
expectedRange, err := semver.ParseRange(r.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if expectedRange(version) {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func jpParseJson(arguments []interface{}) (interface{}, error) {
|
|
input, err := validateArg(parseJson, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var output interface{}
|
|
err = json.Unmarshal([]byte(input.String()), &output)
|
|
return output, err
|
|
}
|
|
|
|
func jpParseYAML(arguments []interface{}) (interface{}, error) {
|
|
input, err := validateArg(parseYAML, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
jsonData, err := yaml.YAMLToJSON([]byte(input.String()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var output interface{}
|
|
err = json.Unmarshal(jsonData, &output)
|
|
return output, err
|
|
}
|
|
|
|
func jpLookup(arguments []interface{}) (interface{}, error) {
|
|
switch input := arguments[0].(type) {
|
|
case map[string]interface{}:
|
|
key, ok := arguments[1].(string)
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, lookup, 2, "String")
|
|
}
|
|
return input[key], nil
|
|
case []interface{}:
|
|
key, ok := arguments[1].(float64)
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, lookup, 2, "Number")
|
|
}
|
|
keyInt, err := intNumber(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"JMESPath function '%s': argument #2: %s",
|
|
lookup, err.Error(),
|
|
)
|
|
}
|
|
if keyInt < 0 || keyInt > len(input)-1 {
|
|
return nil, nil
|
|
}
|
|
return input[keyInt], nil
|
|
default:
|
|
return nil, formatError(invalidArgumentTypeError, lookup, 1, "Object or Array")
|
|
}
|
|
}
|
|
|
|
func jpItems(arguments []interface{}) (interface{}, error) {
|
|
keyName, ok := arguments[1].(string)
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, items, 2, "String")
|
|
}
|
|
valName, ok := arguments[2].(string)
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, items, 3, "String")
|
|
}
|
|
switch input := arguments[0].(type) {
|
|
case map[string]interface{}:
|
|
keys := make([]string, 0, len(input))
|
|
// Sort the keys so that the output is deterministic
|
|
for key := range input {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
arrayOfObj := make([]map[string]interface{}, 0, len(input))
|
|
for _, key := range keys {
|
|
arrayOfObj = append(arrayOfObj, map[string]interface{}{
|
|
keyName: key,
|
|
valName: input[key],
|
|
})
|
|
}
|
|
return arrayOfObj, nil
|
|
case []interface{}:
|
|
arrayOfObj := make([]map[string]interface{}, 0, len(input))
|
|
for index, value := range input {
|
|
arrayOfObj = append(arrayOfObj, map[string]interface{}{
|
|
keyName: float64(index),
|
|
valName: value,
|
|
})
|
|
}
|
|
return arrayOfObj, nil
|
|
default:
|
|
return nil, formatError(invalidArgumentTypeError, items, 1, "Object or Array")
|
|
}
|
|
}
|
|
|
|
func jpObjectFromLists(arguments []interface{}) (interface{}, error) {
|
|
keys, ok := arguments[0].([]interface{})
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, objectFromLists, 1, "Array")
|
|
}
|
|
values, ok := arguments[1].([]interface{})
|
|
if !ok {
|
|
return nil, formatError(invalidArgumentTypeError, objectFromLists, 2, "Array")
|
|
}
|
|
|
|
output := map[string]interface{}{}
|
|
|
|
for i, ikey := range keys {
|
|
key, err := ifaceToString(ikey)
|
|
if err != nil {
|
|
return nil, formatError(invalidArgumentTypeError, objectFromLists, 1, "StringArray")
|
|
}
|
|
if i < len(values) {
|
|
output[key] = values[i]
|
|
} else {
|
|
output[key] = nil
|
|
}
|
|
}
|
|
|
|
return output, nil
|
|
}
|
|
|
|
// InterfaceToString casts an interface to a string type
|
|
func ifaceToString(iface interface{}) (string, error) {
|
|
switch i := iface.(type) {
|
|
case int:
|
|
return strconv.Itoa(i), nil
|
|
case float64:
|
|
return strconv.FormatFloat(i, 'f', -1, 32), nil
|
|
case float32:
|
|
return strconv.FormatFloat(float64(i), 'f', -1, 32), nil
|
|
case string:
|
|
return i, nil
|
|
case bool:
|
|
return strconv.FormatBool(i), nil
|
|
default:
|
|
return "", errors.New("error, undefined type cast")
|
|
}
|
|
}
|
|
|
|
func jpRandom(arguments []interface{}) (interface{}, error) {
|
|
pattern := arguments[0].(string)
|
|
if pattern == "" {
|
|
return "", errors.New("no pattern provided")
|
|
}
|
|
|
|
b := make([]byte, 8)
|
|
_, err := rand.Read(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ans, err := regen.Generate(pattern)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ans, nil
|
|
}
|
|
|
|
func encode[T any](in T) (interface{}, error) {
|
|
buf := new(bytes.Buffer)
|
|
enc := json.NewEncoder(buf)
|
|
if err := enc.Encode(in); err != nil {
|
|
return nil, err
|
|
}
|
|
res := map[string]interface{}{}
|
|
if err := json.Unmarshal(buf.Bytes(), &res); err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func jpX509Decode(arguments []interface{}) (interface{}, error) {
|
|
parseSubjectPublicKeyInfo := func(data []byte) (*rsa.PublicKey, error) {
|
|
spki := cryptobyte.String(data)
|
|
if !spki.ReadASN1(&spki, cryptobyte_asn1.SEQUENCE) {
|
|
return nil, errors.New("writing asn.1 element to 'spki' failed")
|
|
}
|
|
var pkAISeq cryptobyte.String
|
|
if !spki.ReadASN1(&pkAISeq, cryptobyte_asn1.SEQUENCE) {
|
|
return nil, errors.New("writing asn.1 element to 'pkAISeq' failed")
|
|
}
|
|
var spk asn1.BitString
|
|
if !spki.ReadASN1BitString(&spk) {
|
|
return nil, errors.New("writing asn.1 bit string to 'spk' failed")
|
|
}
|
|
if kk, err := x509.ParsePKCS1PublicKey(spk.Bytes); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return kk, nil
|
|
}
|
|
}
|
|
if input, err := validateArg(x509_decode, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if block, _ := pem.Decode([]byte(input.String())); block == nil {
|
|
return nil, errors.New("failed to decode PEM block")
|
|
} else {
|
|
switch block.Type {
|
|
case "CERTIFICATE":
|
|
var cert *x509.Certificate
|
|
if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
|
|
return nil, err
|
|
} else if cert.PublicKeyAlgorithm != x509.RSA {
|
|
return nil, errors.New("certificate should use rsa algorithm")
|
|
} else if pk, err := parseSubjectPublicKeyInfo(cert.RawSubjectPublicKeyInfo); err != nil {
|
|
return nil, errors.New("failed to parse subject public key info")
|
|
} else {
|
|
cert.PublicKey = PublicKey{
|
|
N: pk.N.String(),
|
|
E: pk.E,
|
|
}
|
|
return encode(cert)
|
|
}
|
|
case "CERTIFICATE REQUEST":
|
|
var csr *x509.CertificateRequest
|
|
if csr, err = x509.ParseCertificateRequest(block.Bytes); err != nil {
|
|
return nil, err
|
|
} else if csr.PublicKeyAlgorithm != x509.RSA {
|
|
return nil, errors.New("certificate should use rsa algorithm")
|
|
} else if pk, err := parseSubjectPublicKeyInfo(csr.RawSubjectPublicKeyInfo); err != nil {
|
|
return nil, errors.New("failed to parse subject public key info")
|
|
} else {
|
|
csr.PublicKey = PublicKey{
|
|
N: pk.N.String(),
|
|
E: pk.E,
|
|
}
|
|
return encode(csr)
|
|
}
|
|
default:
|
|
return nil, errors.New("PEM block neither contains a CERTIFICATE or CERTIFICATE REQUEST")
|
|
}
|
|
}
|
|
}
|
|
|
|
func jpImageNormalize(configuration config.Configuration) gojmespath.JpFunction {
|
|
return func(arguments []interface{}) (interface{}, error) {
|
|
if image, err := validateArg(imageNormalize, arguments, 0, reflect.String); err != nil {
|
|
return nil, err
|
|
} else if infos, err := imageutils.GetImageInfo(image.String(), configuration); err != nil {
|
|
return nil, formatError(genericError, imageNormalize, err)
|
|
} else {
|
|
return infos.String(), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func jpSha256(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg("", arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasher := sha256.New()
|
|
_, err = hasher.Write([]byte(str.String()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
hashedBytes := hasher.Sum(nil)
|
|
return hex.EncodeToString(hashedBytes), nil
|
|
}
|
|
|
|
func jpSha1(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg("", arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasher := sha1.New() // #nosec G401
|
|
_, err = hasher.Write([]byte(str.String()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
hashedBytes := hasher.Sum(nil)
|
|
return hex.EncodeToString(hashedBytes), nil
|
|
}
|
|
|
|
func jpMd5(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg("", arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasher := md5.New() // #nosec G401
|
|
_, err = hasher.Write([]byte(str.String()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
hashedBytes := hasher.Sum(nil)
|
|
return hex.EncodeToString(hashedBytes), nil
|
|
}
|