Skip to content

Commit

Permalink
Refactor AAD Unit test to pull from central Jsons (#1151)
Browse files Browse the repository at this point in the history
* reduce copy paste input & add test assertions

* improve debugging messages

* remove unused import

* remove AADConfig var

* update for var fix

* remove unecessary input statement

* refactor add.2.# tests pt 1

* add error for control group

* finish update for aad control group 2

* finish update for aad control group 5

* finish update for aad control group 6

* finish update for aad control group 8

* update TestAssertions for Pass/Fail print statements

* finish update for aad control group 3

* update for aad control group 7 pt 1

* update aad group 7

* update RunUnitTest script pt 1

* update RunUnitTest script pt 2

* finish aad 3

* fix aad.3.3v1

* change to better default DisplayName

* remove unused import

* update github unit test workflow

* fix slightly altered input jsons

* remove unecessary newlines

* fix test_DisplayName_PIM_Correct

* move TestAssertions file

* revert file

* fix 3.2

* remove unused import

* fix linter errors pt 1

* fix linter errors pt 2

* fix linter errors pt 3

* fix linter errors pt 4

* add * to allowed product list

* change ver to verbosity
  • Loading branch information
Sloane4 committed Jun 25, 2024
1 parent 213a912 commit c857b19
Show file tree
Hide file tree
Showing 13 changed files with 2,193 additions and 8,965 deletions.
4 changes: 2 additions & 2 deletions .regal/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rules:
- pattern: '^[A-Z]+[a-zA-Z0-9_]+$|^tests$|^test_|^check_|^error_'
targets:
- rule
- pattern: '^[A-Z]+[a-zA-Z0-9_]+$'
- pattern: '^_*[A-Z]+[a-zA-Z0-9_]+$'
targets:
- function
- pattern: '^[a-z\.]+$|^[a-z\.]+_test$'
Expand Down Expand Up @@ -59,7 +59,7 @@ rules:
# Most of these seem fixable by concatenating strings
# and by using the non-breakable-word-threshold option
# for when that doesn't work.
max-line-length: 120
max-line-length: 150
non-breakable-word-threshold: 100
level: warning
# https://docs.styra.com/regal/rules/style/no-whitespace-comment
Expand Down
12 changes: 4 additions & 8 deletions PowerShell/ScubaGear/Rego/AADConfig.rego
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import data.utils.report.ReportDetailsBoolean
import data.utils.report.ReportDetailsString
import data.utils.key.IsEmptyContainer
import data.utils.key.Contains
import data.utils.key.Count
import data.utils.key.FilterArray
import data.utils.key.ConvertToSetWithKey
import data.utils.key.ConvertToSet
Expand Down Expand Up @@ -211,10 +210,7 @@ tests contains {
#--

# Save all policy names if PhishingResistantMFAPolicies exist
AlternativeMFA contains CAPolicy.DisplayName if {
some CAPolicy in input.conditional_access_policies
Count(PhishingResistantMFAPolicies) > 0
}
AllMFA := AlternativeMFA | PhishingResistantMFAPolicies

# If policy matches basic conditions, special conditions,
# & all exclusions are intentional, save the policy name
Expand All @@ -235,12 +231,12 @@ tests contains {
"PolicyId": "MS.AAD.3.2v1",
"Criticality": "Shall",
"Commandlet": ["Get-MgBetaIdentityConditionalAccessPolicy"],
"ActualValue": AlternativeMFA,
"ReportDetails": concat(". ", [ReportFullDetailsArray(AlternativeMFA, DescriptionString), CAPLINK]),
"ActualValue": AllMFA,
"ReportDetails": concat(". ", [ReportFullDetailsArray(AllMFA, DescriptionString), CAPLINK]),
"RequirementMet": Status
} if {
DescriptionString := "conditional access policy(s) found that meet(s) all requirements"
Status := count(AlternativeMFA) > 0
Status := count(AllMFA) > 0
}
#--

Expand Down
34 changes: 27 additions & 7 deletions PowerShell/ScubaGear/Rego/Utils/KeyFunctions.rego
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package utils.key
import rego.v1
import data.utils.report.ReportDetailsBoolean
import data.test.assert


#############
Expand Down Expand Up @@ -56,21 +57,40 @@ ConvertToSetWithKey(Items, Key) := NewSet if {
###########

# Basic test that has anticipated string for Report Details
TestResult(PolicyId, Output, ReportDetailString, RequirementMet) := true if {
TestResult(PolicyId, Output, ReportDetailStr, Status) := true if {
RuleOutput := [Result | some Result in Output; Result.PolicyId == PolicyId]
count(RuleOutput) == 1
RuleOutput[0].RequirementMet == RequirementMet
RuleOutput[0].ReportDetails == ReportDetailString

# regal ignore: print-or-trace-call
print("\n** Checking if only one test result **")
assert.NotEmpty(RuleOutput)
assert.Equals(1, count(RuleOutput))

# regal ignore: print-or-trace-call
print("** Checking RequirementMet **")
assert.Equals(Status, RuleOutput[0].RequirementMet)

# regal ignore: print-or-trace-call
print("** Checking ReportDetails **")
assert.Equals(ReportDetailStr, RuleOutput[0].ReportDetails)
} else := false

# Test that has multiple strings with an unknown order for Report Details
TestResultContains(PolicyId, Output, ReportDetailArrayStrings, RequirementMet) := true if {
RuleOutput := [Result | some Result in Output; Result.PolicyId == PolicyId]

count(RuleOutput) == 1
RuleOutput[0].RequirementMet == RequirementMet
# regal ignore: print-or-trace-call
print("\n** Checking if only one test result **")
assert.NotEmpty(RuleOutput)
assert.Equals(count(RuleOutput), 1)

# regal ignore: print-or-trace-call
print("** Checking RequirementMet **")
assert.Equals(RuleOutput[0].RequirementMet, RequirementMet)

# regal ignore: print-or-trace-call
print("** Checking ReportDetails **")
Conditions := [
(contains(RuleOutput[0].ReportDetails, String) == true) |
(assert.DoesContains(RuleOutput[0].ReportDetails, String)) |
some String in ReportDetailArrayStrings
]
count(FilterArray(Conditions, false)) == 0
Expand Down
215 changes: 215 additions & 0 deletions PowerShell/ScubaGear/Rego/Utils/TestAssertions.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# METADATA
# description: Utility functions for working with Rego unit tests
# authors:
# - Anders Eknert <[email protected]>
#
package test.assert

import rego.v1

# METADATA
# description: Assert expected is equal to result
Equals(expected, result) if {
expected == result
# regal ignore: print-or-trace-call
print("PASS: expected equals '", expected, "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected equals '", expected, "', got '", result, "'\n")
}

# METADATA
# description: Assert expected is not equal to result
NotEquals(expected, result) if {
expected != result
# regal ignore: print-or-trace-call
print("PASS: expected not equals '", expected, "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected not equals '", expected, "', got '", result, "'\n")
}

# METADATA
# description: Assert all items in coll are equal to value
AllEquals(coll, value) if {
every item in coll {
item == value
}
# regal ignore: print-or-trace-call
print("PASS: expected all items to have value '", _AppendComma(value), "'\n")
} else := false if {
exceptions := [item | some item in coll; item != value]
# regal ignore: print-or-trace-call
print("FAIL: expected all items to have value '", _AppendComma(value), "', failed for '", exceptions, "'\n")
}

# METADATA
# description: Assert no items in coll are equal to value
NoneEquals(coll, value) if {
every item in coll {
item != value
}
# regal ignore: print-or-trace-call
print("PASS: expected no items to have value '", _AppendComma(value), "'\n")
} else := false if {
exceptions := [item | some item in coll; item == value]
# regal ignore: print-or-trace-call
print("FAIL: expected no items to have value '", _AppendComma(value), "', failed for '", exceptions, "'\n")
}

# METADATA
# description: Assert item is in coll
Has(item, coll) if {
item in coll
# regal ignore: print-or-trace-call
print("PASS: expected", type_name(item), _QuoteStr(item), "in", type_name(coll), "\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected", type_name(item), _QuoteStr(item), "in", type_name(coll), "got '", coll, "'\n")
}

# METADATA
# description: Assert item is not in coll
NotHas(item, coll) if {
not item in coll
# regal ignore: print-or-trace-call
print("PASS: did not expect", type_name(item), _QuoteStr(item), "in", type_name(coll), "\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: did not expect", type_name(item), _QuoteStr(item), "in", type_name(coll), "got '", coll, "'\n")
}

# METADATA
# description: Assert provided collection is empty
Empty(coll) if {
count(coll) == 0
# regal ignore: print-or-trace-call
print("PASS: expected empty", type_name(coll), "\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected empty", type_name(coll), "got '", coll, "'\n")
}

# METADATA
# description: Assert provided collection is not empty
NotEmpty(coll) if {
count(coll) != 0
# regal ignore: print-or-trace-call
print("PASS: expected not empty", type_name(coll))
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected not empty", type_name(coll))
}

# METADATA
# description: Assert string starts with search
StartsWith(str, search) if {
startswith(str, search)
# regal ignore: print-or-trace-call
print("PASS: expected '", _QuoteStr(str), "' to start with '", _QuoteStr(search), "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected '", _QuoteStr(str), "' to start with '", _QuoteStr(search), "'\n")
}

# METADATA
# description: Assert string ends with search
EndsWith(str, search) if {
endswith(str, search)
# regal ignore: print-or-trace-call
print("PASS: expected '", _QuoteStr(str), "' to end with '", _QuoteStr(search), "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected '", _QuoteStr(str), "' to end with '", _QuoteStr(search), "'\n")
}

# METADATA
# description: Assert string starts with search
DoesContains(str, search) if {
contains(str, search)
# regal ignore: print-or-trace-call
print("PASS: expected '", _QuoteStr(str), "' to contain '", _QuoteStr(search), "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected '", _QuoteStr(str), "' to contain '", _QuoteStr(search), "'\n")
}

# METADATA
# description: Assert string ends with search
NotContains(str, search) if {
not contains(str, search)
# regal ignore: print-or-trace-call
print("PASS: expected '", _QuoteStr(str), "' to not contain '", _QuoteStr(search), "'\n")
} else := false if {
# regal ignore: print-or-trace-call
print("FAIL: expected '", _QuoteStr(str), "' to not contain '", _QuoteStr(search), "'\n")
}

# METADATA
# description: Assert all strings in coll starts with search
AllStartsWith(coll, search) if {
every str in coll {
startswith(str, search)
}
# regal ignore: print-or-trace-call
print("PASS: expected all strings to start with '", _AppendComma(search), "'\n")
} else := false if {
exceptions := [str | some str in coll; not startswith(str, search)]
# regal ignore: print-or-trace-call
print("FAIL: expected all strings to start with '", _AppendComma(search), "' failed for '", exceptions, "'\n")
}

# METADATA
# description: Assert all strings in coll ends with search
AllEndsWith(coll, search) if {
every str in coll {
endswith(str, search)
}
# regal ignore: print-or-trace-call
print("PASS: expected all strings to end with '", _AppendComma(search), "'\n")
} else := false if {
exceptions := [str | some str in coll; not endswith(str, search)]
# regal ignore: print-or-trace-call
print("FAIL: expected all strings to end with '", _AppendComma(search), "' failed for '", exceptions, "'\n")
}

# METADATA
# description: Assert no strings in coll starts with search
NoneStartsWith(coll, search) if {
every str in coll {
not startswith(str, search)
}
# regal ignore: print-or-trace-call
print("PASS: expected no strings to start with '", _AppendComma(search), "'\n")
} else := false if {
exceptions := [str | some str in coll; startswith(str, search)]
# regal ignore: print-or-trace-call
print("FAIL: expected no strings to start with '", _AppendComma(search), "' failed for '", exceptions, "'\n")
}

# METADATA
# description: Assert no strings in coll ends with search
NoneEndsWith(coll, search) if {
every str in coll {
not endswith(str, search)
}
# regal ignore: print-or-trace-call
print("PASS: expected no strings to end with '", _AppendComma(search), "'\n")
} else := false if {
exceptions := [str | some str in coll; endswith(str, search)]
# regal ignore: print-or-trace-call
print("FAIL: expected no strings to end with '", _AppendComma(search), "' failed for '", exceptions, "'\n")
}

# METADATA
# description: Fail with provided message
Fail(msg) := [][0] if {
# regal ignore: print-or-trace-call
print(msg)
}

_QuoteStr(x) := concat("", [`"`, x, `"`]) if is_string(x)

_QuoteStr(x) := x if not is_string(x)

_AppendComma(str) := sprintf("%v,", [_QuoteStr(str)])
Loading

0 comments on commit c857b19

Please sign in to comment.