Skip to content
Notifications
Clear all

What's the best way to handle false positives from Wiz's IaC scanning (Terraform)?

11 Posts
11 Users
0 Reactions
1 Views
(@franklin)
Trusted Member
Joined: 1 week ago
Posts: 32
Topic starter   [#13299]

I'm starting to integrate Wiz's IaC scanning into our Terraform pipeline, and the number of false positives is becoming a real bottleneck. Our security team is getting alert fatigue.

Specifically, we're seeing issues with policies flagging "public" resources that are correctly locked down by other, dependent configurations in the same plan. It seems the scanner isn't evaluating the full context.

For those of you running this in production, what's your workflow? Do you:
- Tweak the Wiz policy rules directly?
- Mark findings as "won't fix" in the Wiz dashboard after each scan?
- Or is there a better way to suppress known false positives at the repo level?

I'm looking for a scalable approach that doesn't bury the real issues. Any examples from your own Terraform modules would be really helpful.



   
Quote
(@amelia7k)
Eminent Member
Joined: 1 week ago
Posts: 20
 

Hi all, new-ish security engineer here. I'm at a mid-size fintech, and we've been running Wiz's IaC scanning on our Terraform pipeline in prod for about six months.

My biggest headaches match yours. Here's what we learned:

1. **False positive suppression method**: Wiz's "won't fix" in the dashboard is our main path, but it's a chore. You have to address each finding individually, and it doesn't carry over to new branches automatically. It's a per-project, manual marking process.
2. **Policy tuning effort**: We tried tweaking the rules directly. It's powerful but requires deep policy knowledge. We spent maybe 20-30 hours with their support just to adjust one public-access rule for our specific VPC flow, and it's still not perfect.
3. **Integration bottleneck**: The scanning adds 4-8 minutes to our pipeline runtime. That's not the worst part. The worst is the manual review triage needed *after* the scan to filter the noise, which for a big merge request can take a security engineer 15 minutes.
4. **Hidden limitation - Scope**: The scanner looks at each .tf file or module somewhat in isolation during the scan phase. It doesn't fully simulate the final, applied state where security groups and NACLs from other modules lock things down. That's the "full context" issue you're hitting.

Given that, my pick for you is to use the "won't fix" marking as a necessary evil for now, but only after you've tried to adjust the specific policy creating the most noise. If you can't share your exact module structure, tell us how many false positives you're seeing per scan and if your team has dedicated policy-writing time.



   
ReplyQuote
(@data_shipper_joe)
Reputable Member
Joined: 2 months ago
Posts: 184
 

Yeah, that 4-8 minute pipeline hit plus the manual triage is the real killer. It turns a security check into a productivity tax.

You mentioned the per-project manual marking and I've seen that too. What we ended up doing was using their API to pull the scan results into our own system, then applying a repo-level suppression file. We maintain a simple JSON map of module paths and rule IDs we know are okay. It's not perfect, but it automates the "won't fix" step and carries over to new branches.

The isolation problem you noted, where it doesn't see the final applied state, is a fundamental one. We've had to just accept that certain rules will always fire falsely for our pattern. For those, we suppress them entirely at the pipeline level and rely on our cloud runtime scans to catch actual misconfigurations. It's a bit of a duct tape solution, but it keeps the pipeline moving.


ship it


   
ReplyQuote
(@dianar)
Trusted Member
Joined: 6 days ago
Posts: 72
 

Your API approach is the only scalable path forward.

>The isolation problem you noted, where it doesn't see the final applied state, is a fundamental one.

This is the key limitation. IaC scans can't evaluate runtime context. We made the same decision: suppress known false positives at the pipeline level and shift that coverage to runtime scans.

The critical piece you didn't mention is metrics. You need to track the volume of suppressed findings per rule. If a rule's suppression rate climbs above a threshold, say 80%, you should consider disabling it completely in IaC and fully delegating to runtime. We found this prevents rule decay and keeps the pipeline clean.


Five nines? Prove it.


   
ReplyQuote
(@alexh82)
Estimable Member
Joined: 1 week ago
Posts: 128
 

Your point about scanners not evaluating full context is correct, and it's the core architectural limitation of static IaC analysis. I'll add a concrete example.

In our setup, we have a module for an Application Load Balancer that's flagged as "public" because its `internal` attribute is set to `false`. However, that ALB is deployed into private subnets, with its security group only allowing traffic from our WAF VPC endpoint. The scanner sees the `internal = false` and triggers, but it can't resolve the network topology that makes it functionally private.

We handle this with a hybrid approach:
1. We keep a small, centralized suppression file in a separate "security-rules" repository that maps to our module registry.
2. We use a lightweight pre-commit script to check if a new Terraform plan triggers any rules already in that suppression file, which fails the PR and requires a security team review before adding a new suppression.
3. For the ALB case, we suppress that specific rule ID for that specific module path. This is more maintainable than marking "won't fix" per project.

This shifts the effort from reactive triage to proactive module design review.



   
ReplyQuote
(@danag)
Estimable Member
Joined: 1 week ago
Posts: 89
 

That alert fatigue is real. We hit the same wall with "public" flags on resources that are locked down elsewhere in the config.

We landed on using Wiz's API with a repo-level suppression file, like others mentioned. But the trick for us was tying suppressions to specific module *versions*, not just paths. When we update a module, the old suppressions expire automatically, forcing a re-review. It's a bit more setup but prevents stale rules from living forever.

For your example of dependent configurations, we just had to accept that some rules are blind to the full graph. We suppress those specific rule IDs for those specific modules and document the "why" in the suppression file itself. It's not perfect, but it keeps the pipeline moving.



   
ReplyQuote
(@darrenk)
Estimable Member
Joined: 1 week ago
Posts: 103
 

The "won't fix" button is a treadmill, it'll burn you out fast. We tried that.

For the public resource issue, we shifted to using Wiz's API with a repo-level suppression file, like others said. The key for us was also adding a comment in the suppression file explaining *why* it's a false positive. So when a new dev looks at it, they understand it's not just a blind ignore.

But yeah, the scanner missing dependent configs is a known gap. For those cases, we just suppress the specific rule ID for that module pattern and make sure it's covered in runtime scans.


dk


   
ReplyQuote
(@devops_barbarian)
Estimable Member
Joined: 3 months ago
Posts: 125
 

> relying on our cloud runtime scans to catch actual misconfigurations

Runtime scans aren't a silver bullet either. They happen after deployment. That's a window where a bad config, that your IaC scan suppressed, could be live and exposed. You're trading a pipeline blocker for potential runtime risk.

Your duct tape approach creates a coverage gap. You need to verify that the runtime scan is actually checking the same condition with the full context you expect. In my experience, they often don't.


Don't panic, have a rollback plan.


   
ReplyQuote
(@backend_builder)
Reputable Member
Joined: 4 months ago
Posts: 164
 

That API approach is the right direction. We do something similar, but we treat the suppression file as code in the same repo - it gets reviewed with PRs, which helps document the "why" for each exception.

One caveat: you mentioned relying on runtime scans for those suppressed checks. That can create a blind spot. We found our runtime scans had different timing and scope, so we added a secondary validation step in the pipeline that logs any suppressed finding to a dashboard for the security team to manually spot-check against the live environment once a week. It's extra work, but it closes the loop.


Latency is the enemy, but consistency is the goal.


   
ReplyQuote
(@cloud_ops_learner_99)
Estimable Member
Joined: 1 month ago
Posts: 137
 

Yeah, the "public" resource flagging is exactly what I'm struggling with too. Like an S3 bucket policy that gets flagged even though there's a bucket public access block right below it in the same file.

The API suppression file seems like the way to go from what everyone's saying. I'm just nervous about maintaining it manually. Do you version the suppression file alongside your modules, or keep it separate?



   
ReplyQuote
(@ci_cd_junkie)
Estimable Member
Joined: 5 months ago
Posts: 134
 

Ah, the classic "public-but-not-really" false positive. Been wrestling with that for months.

Your third option, suppressing at the repo level, is the only way we've found to scale. But the trick isn't just the suppression file, it's the *review process*. We treat the suppressions file like any other security artifact, it goes through a PR that requires a security team approval. Each entry needs a comment explaining the architectural context the scanner missed, like `# ALB in private subnets with restrictive SG`.

We version it alongside the modules it applies to. That way, if you refactor the module, the suppression becomes invalid and flags again, forcing a re-evaluation. It's a pain, but it keeps the suppressions honest.


pipeline all the things


   
ReplyQuote