# Exercise 2: Implement an IAM Policy Evaluator

```
STEP 1. Save the JavaScript below as `iam_evaluator.js` in a scratch folder.
STEP 2. Implement the `evaluate(policies, action, resource)` function
        following the REAL evaluation order:
          a. Collect every statement from every policy that matches
             (by Action and Resource — an exact-string match is enough
             for this exercise, no wildcard matching required).
          b. If ANY matching statement has Effect "Deny" -> return "Deny".
          c. Else if ANY matching statement has Effect "Allow" -> return "Allow".
          d. Else -> return "Deny" (implicit default deny).
STEP 3. Run `node iam_evaluator.js` and confirm every line prints PASS.
STEP 4. Do NOT reorder the test cases or "fix" the expected values —
        they encode the real AWS evaluation rules from the theory file.
```

```js
// iam_evaluator.js

function evaluate(policies, action, resource) {
  // TODO: implement per the algorithm in the header comment above.
  // `policies` is an array of policy documents, each:
  //   { Statement: [ { Effect: "Allow" | "Deny", Action: string, Resource: string }, ... ] }
  // Return the string "Allow" or "Deny".
}

// --- self-check: do not modify below this line ---

const tests = [
  {
    name: "no policies at all -> implicit deny",
    policies: [],
    action: "s3:GetObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Deny",
  },
  {
    name: "single Allow matching -> Allow",
    policies: [
      { Statement: [{ Effect: "Allow", Action: "s3:GetObject", Resource: "arn:aws:s3:::bucket/file" }] },
    ],
    action: "s3:GetObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Allow",
  },
  {
    name: "Allow in one policy + no match in another -> Allow (irrelevant policy ignored)",
    policies: [
      { Statement: [{ Effect: "Allow", Action: "s3:GetObject", Resource: "arn:aws:s3:::bucket/file" }] },
      { Statement: [{ Effect: "Allow", Action: "ec2:StartInstances", Resource: "*" }] },
    ],
    action: "s3:GetObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Allow",
  },
  {
    name: "Allow AND Deny both match the same action/resource -> Deny wins, always",
    policies: [
      { Statement: [{ Effect: "Allow", Action: "s3:DeleteObject", Resource: "arn:aws:s3:::bucket/file" }] },
      { Statement: [{ Effect: "Deny", Action: "s3:DeleteObject", Resource: "arn:aws:s3:::bucket/file" }] },
    ],
    action: "s3:DeleteObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Deny",
  },
  {
    name: "Deny appears BEFORE Allow in document order -> still Deny (order is irrelevant)",
    policies: [
      { Statement: [{ Effect: "Deny", Action: "s3:DeleteObject", Resource: "arn:aws:s3:::bucket/file" }] },
      { Statement: [{ Effect: "Allow", Action: "s3:DeleteObject", Resource: "arn:aws:s3:::bucket/file" }] },
    ],
    action: "s3:DeleteObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Deny",
  },
  {
    name: "ten Allow statements cannot outvote a single Deny",
    policies: [
      { Statement: Array.from({ length: 10 }, () => ({ Effect: "Allow", Action: "s3:PutObject", Resource: "arn:aws:s3:::bucket/file" })) },
      { Statement: [{ Effect: "Deny", Action: "s3:PutObject", Resource: "arn:aws:s3:::bucket/file" }] },
    ],
    action: "s3:PutObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Deny",
  },
  {
    name: "statements for a different resource don't count as a match -> implicit deny",
    policies: [
      { Statement: [{ Effect: "Allow", Action: "s3:GetObject", Resource: "arn:aws:s3:::other-bucket/file" }] },
    ],
    action: "s3:GetObject",
    resource: "arn:aws:s3:::bucket/file",
    expected: "Deny",
  },
];

let failures = 0;
for (const t of tests) {
  const result = evaluate(t.policies, t.action, t.resource);
  const pass = result === t.expected;
  if (!pass) failures++;
  console.log(`${pass ? "PASS" : "FAIL"} - ${t.name} (expected ${t.expected}, got ${result})`);
}
console.log(failures === 0 ? "\nAll tests passed." : `\n${failures} test(s) failed.`);
```
