File: 08-2026-07-08-starting-pontoanticrack.md
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200

Starting PontoAntiCrack in dry-run

Published: 2026-07-08 // Root Access: Granted

Starting PontoAntiCrack in dry-run

PontoAntiCrack is allowed to be wrong before it is allowed to be powerful.

That is why the first deployment mode is dry-run.

The system will receive AWS events, decide whether they represent a real problem, inspect the current resource, save the original state, record what it would do, and send an alert. It will not change the resource yet.

An automated remediation tool can become an incident by itself. A broad EventBridge pattern and a Lambda with write permission are enough to modify hundreds of resources for the wrong reason. I want the system to earn its write path with real event data.

Three detections, not one universal handler

The first version contains three detection units.

s3-public handles changes that can make an S3 bucket public.

sg-open handles Security Group rules that expose dangerous services to the internet, with SSH as the clearest case.

iam-key-leak handles AWS findings that indicate compromised IAM credentials.

Each unit owns its EventBridge pattern, handler, IAM permissions, event fixtures, positive tests, negative tests, documentation, and MITRE ATT&CK mapping.

I considered a generic "AWS security event" handler with a large set of conditionals. It would reduce the number of Lambda packages. It would also make least privilege harder and allow an S3 detection to inherit EC2 or IAM permissions it never uses.

Separate functions are slightly repetitive. The permission boundary is easier to see.

The shared pipeline

The detections share an execution order.

First, the handler inspects the event and current resource through plan(). A plan can return no action when the event is benign or the resource is already safe.

Next, the pipeline checks whether the resource carries the documented exclusion tag. An exclusion is visible configuration, not a silent exception in code.

If action is needed, the original state is written to the audit store before anything changes. The snapshot includes enough context for investigation and, where appropriate, rollback.

The circuit breaker checks the recent remediation count. If the count is too high, the system stops applying changes and records why.

Dry-run is evaluated after the snapshot and breaker. This is deliberate. A dry-run event still tells me how frequently the system would have remediated, so it must contribute to the rate limit.

Only then can apply() execute the plan. The audit record is closed with the result, and the notification is sent.

The sequence is fixed by the shared runner. A new detection should not be able to skip the snapshot because its author forgot one call.

Why plan() is separate from apply()

I want the decision to be inspectable before it becomes an action.

plan() reads the event and current AWS state. It returns a structured description of the problem, the resource, the intended change, and the evidence needed by the audit record.

apply() receives that plan. It should not reinterpret the whole event or silently widen the action.

This split gives dry-run a real job. Dry-run is not a log statement saying "I would remediate." It executes the same planning path that enforcement will use and stops at the mutation boundary.

It also makes negative tests clearer. A benign event should produce no plan. If it produces a plan and apply() happens to do nothing, the detector is still noisy.

The negative tests matter more than the obvious ones

An open SSH rule for 0.0.0.0/0 should match. That is the easy test.

The harder cases are:

  1. SSH allowed only from 10.0.0.0/8 must remain unchanged.

  2. HTTPS on port 443 open to the internet should not be treated like an exposed administration port.

  3. A Security Group permission containing both a private range and a public range should lose only the unsafe range.

  4. An S3 policy using a wildcard principal but restricting access to the AWS organization should not be classified the same as anonymous public access.

  5. A low-severity finding unrelated to access keys should not reach the credential handler.

  6. A root credential case should never trigger automatic root remediation.

A detector can pass every positive test while matching nearly everything. The negative fixtures are the evidence that the boundary exists.

S3 policy conditions are already uncomfortable

S3 bucket policies are the detection I expect to revise most.

A wildcard principal may be public. It may also be limited by a strong condition such as aws:PrincipalOrgID. A weak condition can look reassuring without restricting the caller. Requiring TLS changes transport behavior. It does not turn the internet into a private principal.

I do not want a rule that treats every Condition as safe. I also do not want a rule that removes legitimate cross-account or organization access because it sees "Principal": "*".

The first implementation will classify the conditions it actually understands and escalate ambiguous cases rather than guessing. "No safe automated action" is a valid result. It is better than pretending every finding can be closed by Lambda.

Root is a human decision

The IAM credential detection treats root differently.

If a finding involves a normal access key, the handler can follow the bounded containment plan implemented for that key type.

If the finding involves root credentials, the system records and escalates it. It does not attempt an automatic fix.

Root recovery can involve account-level actions, contact paths, MFA, and decisions outside the evidence available to one Lambda. Automating a guess there would violate the same safety principle the project is meant to demonstrate.

Slack is the last step

Notifications are useful, but they are not the audit system.

The durable record lives in DynamoDB with the original snapshot, planned action, mode, result, and relevant identifiers. Slack receives a concise summary after the pipeline has updated that record.

The notification must not include credential material. It should contain enough to locate the audit entry without copying sensitive event fields into a chat system.

If Slack is unavailable, remediation state still needs to be correct. A webhook should not decide whether an AWS control is safe.

What I need to prove before enforcement

The repository begins with fixtures based on AWS documentation. That is useful for implementation and insufficient for deployment.

Before I disable dry-run, I need:

  1. A real event for each CloudTrail-based detection.

  2. A real finding shape for the credential path.

  3. EventBridge pattern tests using the official API.

  4. Evidence that benign nearby events do not match.

  5. A measured remediation count under dry-run.

  6. A circuit-breaker test.

  7. An audit snapshot captured before any lab resource changes.

The AwLZ Lab account gives me a controlled place to do this. Its events already flow through the organization trail, and its security findings are centrally visible.

For now, PontoAntiCrack can watch, plan, remember, and complain in Slack.

It cannot touch anything.

That is a feature.