aws_iam_compliance_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package policy
  2. import (
  3. "testing"
  4. "github.com/stretchr/testify/assert"
  5. )
  6. func TestAWSIAMMatch(t *testing.T) {
  7. evalCtx := &EvaluationContext{
  8. RequestContext: map[string]interface{}{
  9. "aws:username": "testuser",
  10. "saml:username": "john.doe",
  11. "oidc:sub": "user123",
  12. "aws:userid": "AIDACKCEVSQ6C2EXAMPLE",
  13. "aws:principaltype": "User",
  14. },
  15. }
  16. tests := []struct {
  17. name string
  18. pattern string
  19. value string
  20. evalCtx *EvaluationContext
  21. expected bool
  22. }{
  23. // Case insensitivity tests
  24. {
  25. name: "case insensitive exact match",
  26. pattern: "S3:GetObject",
  27. value: "s3:getobject",
  28. evalCtx: evalCtx,
  29. expected: true,
  30. },
  31. {
  32. name: "case insensitive wildcard match",
  33. pattern: "S3:Get*",
  34. value: "s3:getobject",
  35. evalCtx: evalCtx,
  36. expected: true,
  37. },
  38. // Policy variable expansion tests
  39. {
  40. name: "AWS username variable expansion",
  41. pattern: "arn:aws:s3:::mybucket/${aws:username}/*",
  42. value: "arn:aws:s3:::mybucket/testuser/document.pdf",
  43. evalCtx: evalCtx,
  44. expected: true,
  45. },
  46. {
  47. name: "SAML username variable expansion",
  48. pattern: "home/${saml:username}/*",
  49. value: "home/john.doe/private.txt",
  50. evalCtx: evalCtx,
  51. expected: true,
  52. },
  53. {
  54. name: "OIDC subject variable expansion",
  55. pattern: "users/${oidc:sub}/data",
  56. value: "users/user123/data",
  57. evalCtx: evalCtx,
  58. expected: true,
  59. },
  60. // Mixed case and variable tests
  61. {
  62. name: "case insensitive with variable",
  63. pattern: "S3:GetObject/${aws:username}/*",
  64. value: "s3:getobject/testuser/file.txt",
  65. evalCtx: evalCtx,
  66. expected: true,
  67. },
  68. // Universal wildcard
  69. {
  70. name: "universal wildcard",
  71. pattern: "*",
  72. value: "anything",
  73. evalCtx: evalCtx,
  74. expected: true,
  75. },
  76. // Question mark wildcard
  77. {
  78. name: "question mark wildcard",
  79. pattern: "file?.txt",
  80. value: "file1.txt",
  81. evalCtx: evalCtx,
  82. expected: true,
  83. },
  84. // No match cases
  85. {
  86. name: "no match different pattern",
  87. pattern: "s3:PutObject",
  88. value: "s3:GetObject",
  89. evalCtx: evalCtx,
  90. expected: false,
  91. },
  92. {
  93. name: "variable not expanded due to missing context",
  94. pattern: "users/${aws:username}/data",
  95. value: "users/${aws:username}/data",
  96. evalCtx: nil,
  97. expected: true, // Should match literally when no context
  98. },
  99. }
  100. for _, tt := range tests {
  101. t.Run(tt.name, func(t *testing.T) {
  102. result := awsIAMMatch(tt.pattern, tt.value, tt.evalCtx)
  103. assert.Equal(t, tt.expected, result, "AWS IAM match result should match expected")
  104. })
  105. }
  106. }
  107. func TestExpandPolicyVariables(t *testing.T) {
  108. evalCtx := &EvaluationContext{
  109. RequestContext: map[string]interface{}{
  110. "aws:username": "alice",
  111. "saml:username": "alice.smith",
  112. "oidc:sub": "sub123",
  113. },
  114. }
  115. tests := []struct {
  116. name string
  117. pattern string
  118. evalCtx *EvaluationContext
  119. expected string
  120. }{
  121. {
  122. name: "expand aws username",
  123. pattern: "home/${aws:username}/documents/*",
  124. evalCtx: evalCtx,
  125. expected: "home/alice/documents/*",
  126. },
  127. {
  128. name: "expand multiple variables",
  129. pattern: "${aws:username}/${oidc:sub}/data",
  130. evalCtx: evalCtx,
  131. expected: "alice/sub123/data",
  132. },
  133. {
  134. name: "no variables to expand",
  135. pattern: "static/path/file.txt",
  136. evalCtx: evalCtx,
  137. expected: "static/path/file.txt",
  138. },
  139. {
  140. name: "nil context",
  141. pattern: "home/${aws:username}/file",
  142. evalCtx: nil,
  143. expected: "home/${aws:username}/file",
  144. },
  145. {
  146. name: "missing variable in context",
  147. pattern: "home/${aws:nonexistent}/file",
  148. evalCtx: evalCtx,
  149. expected: "home/${aws:nonexistent}/file", // Should remain unchanged
  150. },
  151. }
  152. for _, tt := range tests {
  153. t.Run(tt.name, func(t *testing.T) {
  154. result := expandPolicyVariables(tt.pattern, tt.evalCtx)
  155. assert.Equal(t, tt.expected, result, "Policy variable expansion should match expected")
  156. })
  157. }
  158. }
  159. func TestAWSWildcardMatch(t *testing.T) {
  160. tests := []struct {
  161. name string
  162. pattern string
  163. value string
  164. expected bool
  165. }{
  166. {
  167. name: "case insensitive asterisk",
  168. pattern: "S3:Get*",
  169. value: "s3:getobject",
  170. expected: true,
  171. },
  172. {
  173. name: "case insensitive question mark",
  174. pattern: "file?.TXT",
  175. value: "file1.txt",
  176. expected: true,
  177. },
  178. {
  179. name: "mixed wildcards",
  180. pattern: "S3:*Object?",
  181. value: "s3:getobjects",
  182. expected: true,
  183. },
  184. {
  185. name: "no match",
  186. pattern: "s3:Put*",
  187. value: "s3:GetObject",
  188. expected: false,
  189. },
  190. }
  191. for _, tt := range tests {
  192. t.Run(tt.name, func(t *testing.T) {
  193. result := AwsWildcardMatch(tt.pattern, tt.value)
  194. assert.Equal(t, tt.expected, result, "AWS wildcard match should match expected")
  195. })
  196. }
  197. }