Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(query): optimized passwords and secrets query #3059

Merged
merged 3 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,108 @@ import data.generic.common as commonLib

# search for harcoded secrets by looking for their values with a special chars and length
CxPolicy[result] {
docs := input.document[id]
treatedDoc = checkName(docs, name, docs[name])
keyDoc = treatedDoc[y][father]

clearParse := ["playbooks", "tasks", "command"]
not commonLib.equalsOrInArray(clearParse, father)

#get all string values from json
allValues = regex.find_n("\"[^\"]+\"\\s*:\\s*\"[^\"]+\"[]\n\r,}]", json.marshal(keyDoc), -1)

correctStrings := getCorrectStrings(replaceUniCode(allValues[m]), father)

checkforvulnerability(correctStrings)

docs := input.document[_]

[path, value] = walk(docs)
is_string(value)
value = replace_unicode(value)
checkObjects := prepare_object(path[minus(count(path), 1)], value)
checkObject := checkObjects[_]
check_vulnerability(checkObject)
allPath := [x | merge_path(path[i]) != ""; x := merge_path(path[i])]
result := {
"documentId": docs.id,
"searchKey": sprintf("{{%s}}.%s={{%s}}", [correctStrings.id, correctStrings.key, correctStrings.value]),
"searchKey": resolve_path(checkObject, allPath),
"issueType": "RedundantAttribute",
"keyExpectedValue": "Hardcoded secret key should not appear in source",
"keyActualValue": correctStrings.value,
"keyActualValue": value,
}
}

merge_path(pathItem) = item {
not is_string(pathItem)
item := ""
} else = item {
clearParse := ["playbooks", "tasks", "command", "original"]
commonLib.equalsOrInArray(clearParse, lower(pathItem))
item := ""
} else = item {
contains(pathItem, ".")
item := sprintf("{{%s}}", [pathItem])
} else = item {
item := pathItem
}

resolve_path(obj, path) = resolved {
obj.id != ""
resolved := sprintf("FROM=%s.{{%s}}", [concat(".", path), obj.id])
} else = resolved {
resolved := concat(".", path)
}

prepare_object(key, value) = obj {
#dockerfile
key == "Original"
args := split(value, " ")
obj := [x | x := create_docker_object(args[_], value)]
} else = obj {
obj := [{
"key": key,
"value": value,
"id": "",
}]
}

create_docker_object(value, original) = obj {
contains(value, "=")
splitted := split(value, "=")
count(splitted) > 1
k := splitted[0]
is_string(k)
v := concat("", array.slice(splitted, 1, count(splitted)))
obj := {
"key": k,
"value": v,
"id": original,
}
}

isUnderPasswordKey(p) {
is_under_password_key(p) {
ar = {"pas", "psw", "pwd"}
contains(lower(p), ar[_])
}

isUnderSekretKey(p) = res {
is_under_secret_key(p) = res {
ar = {"secret", "encrypt", "credential"}
res := contains(lower(p), ar[_])
}

#search for default passwords
checkforvulnerability(correctStrings) {
check_vulnerability(correctStrings) {
commonLib.isDefaultPassword(correctStrings.value)
isUnderPasswordKey(correctStrings.key)
is_under_password_key(correctStrings.key)

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

#search for non-default passwords under known names
checkforvulnerability(correctStrings) {
check_vulnerability(correctStrings) {
#remove short strings
count(correctStrings.value) > 4
count(correctStrings.value) < 30

#password should contain alpha and numeric and not contain spaces
count(regex.find_n("[a-zA-Z0-9]+", correctStrings.value, -1)) > 0
count(regex.find_n("^[^{{]+$", correctStrings.value, -1)) > 0
isUnderPasswordKey(correctStrings.key)
is_under_password_key(correctStrings.key)

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

#search for non-default passwords with upper, lower chars and digits
checkforvulnerability(correctStrings) { #ignore ascii cases
check_vulnerability(correctStrings) { #ignore ascii cases
#remove short strings
count(correctStrings.value) > 6
count(correctStrings.value) < 20
Expand All @@ -74,49 +117,49 @@ checkforvulnerability(correctStrings) { #ignore ascii cases
count(regex.find_n("^[^\\s_]+", correctStrings.value, -1)) > 0

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

#search for harcoded secrets with known prefixes
checkforvulnerability(correctStrings) {
check_vulnerability(correctStrings) {
#look for a known prefix
contains(correctStrings.value, "PRIVATE KEY")

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

#search for harcoded secret keys under known names
checkforvulnerability(correctStrings) {
check_vulnerability(correctStrings) {
#remove short strings
count(correctStrings.value) > 8

#remove string with non-keys characters
count(regex.find_n("^[^\\s$]+$", correctStrings.value, -1)) > 0

#look for a known names
isUnderSekretKey(correctStrings.key)
is_under_secret_key(correctStrings.key)

#remove string with non-keys characters
count(regex.find_n("^[^\\s/:@,.-_|]+$", correctStrings.value, -1)) > 0

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

#search for harcoded secrets by looking for their values with a special chars and length
checkforvulnerability(correctStrings) {
check_vulnerability(correctStrings) {
#remove short strings
count(correctStrings.value) > 30

#remove string with non-keys characters
count(regex.find_n("^[^\\s/:@,.-_|]+$", correctStrings.value, -1)) > 0

#remove common key and values
checkCommon(correctStrings)
check_common(correctStrings)
}

checkCommon(correctStrings) {
check_common(correctStrings) {
#remove common values
not commonLib.isCommonValue(correctStrings.value)

Expand All @@ -125,47 +168,7 @@ checkCommon(correctStrings) {
}

#replace unicode values to avoid false positives
replaceUniCode(allValues) = treatedValue {
replace_unicode(allValues) = treatedValue {
treatedValue_first := replace(allValues, "\\u003c", "<")
treatedValue = replace(treatedValue_first, "\\u003e", ">")
}

#construct correct strings based on dockerfile or other platform
getCorrectStrings(allValues, name) = correctStrings {
#other platforms
not contains(split(allValues, "\":")[0], "Original")
allStrings = {"key": split(allValues, "\":")[0], "value": split(allValues, "\":")[1]}

#remove trailling quotation marks
correctStrings = {
"key": substring(allStrings.key, 1, count(allStrings.key) - 1),
"value": substring(allStrings.value, 1, count(allStrings.value) - 3),
"id": name,
}
} else = correctStrings {
#dockerfile
contains(split(allValues, "\":")[0], "Original")

#replace "=" with spaces to check for problems with for the various formats of ENV in dockerfile
treatedValue := replace(allValues, "=", " ")
allStrings := {"key": split(split(treatedValue, ":")[1], " ")[1], "value": split(split(treatedValue, ":")[1], " ")[2]}

#remove trailling quotation marks
correctStrings := {
"key": substring(allStrings.key, 0, count(allStrings.key)),
"value": substring(allStrings.value, 0, count(allStrings.value) - 2),
"id": name,
}
}

# clear parser added feilds
checkName(document, name, docName) = treatedName {
clearAns := ["playbooks", "tasks"]
commonLib.equalsOrInArray(clearAns, name)
treatedName := [x | x := docName[int]]
} else = treatedName {
name == "command"
treatedName := [x | x := docName]
} else = treatedName {
treatedName := [x | x := document]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ resource "google_container_cluster" "primary" {
initial_node_count = 3

master_auth {
username = ""
password = ""

client_certificate_config {
issue_client_certificate = true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
FROM baseImage

ARG password=pass!1213Fs


FROM test2
ARG password=pass!1213Fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,24 @@
"line": 29,
"fileName": "positive4.tf"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
"line": 49,
"fileName": "positive4.tf"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
"line": 69,
"fileName": "positive4.tf"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
"line": 89,
"fileName": "positive4.tf"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
Expand All @@ -53,6 +65,12 @@
"line": 3,
"fileName": "positive5.dockerfile"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
"line": 7,
"fileName": "positive5.dockerfile"
},
{
"queryName": "Passwords And Secrets In Infrastructure Code",
"severity": "HIGH",
Expand Down