DataDog / datadog-static-analyzer

Datadog Static Analyzer
https://docs.datadoghq.com/static_analysis/
Apache License 2.0
100 stars 12 forks source link

[STAL-2289] feat: add terraform file context helper in the JS code #409

Closed amaanq closed 3 months ago

amaanq commented 3 months ago

What problem are you trying to solve?

Currently, implementing a rule that checks for the existence or validity of a similar resource is nearly impossible, and is very difficult to express in the query syntax. The issue that sparked this PR was that given a specific resource, with a type of foo, a user wanted to check that a corresponding resource with the typefoo_public_access_block existed. Implementing this with a query is very difficult with a lot of edge cases and loop holes to consider (e.g. does this corresponding resource come before or after the main one, is it immediately after, etc), and just having all the resources in a given Terraform module exposed nicely in the JS api would be much more preferable, in a way where a user can simply just check every resource and their type and name in a given Terraform module.

What is your solution?

The solution is to create a file context for terraform files that exposes a resources array for users to interact with in the JS rule code. It is similar to what we currently do for Golang, but instead of having a map of aliases to resolved package names, we have an array of resources, where each Resource is an object with a type and name field.

For example, given the following TF module

resource "aws_s3_bucket" "unsafe-bucket" {
  region       = "us-east-1"
  bucket        = "my_bucket"
  acl           = "public-read"
  force_destroy = 3
  tags {
    Name = "my-bucket"
  }
}

resource "aws_s3_bucket_public_access_block" "access" {
  bucket = aws_s3_bucket.safe-bucket.id
}

The resources array would look like the following, as a JS object:

[
  { type: "aws_s3_bucket", name: "unsafe-bucket" },
  { type: "aws_s3_bucket_public_acccess_block", name: "access" }
]

A user can now query for only one resource, and have access to all the other resources available in the module, instead of having to write complex queries to capture two resources, where the order might not matter but is difficult to express in a tree-sitter query.

Users also have access to some helper functions that they can use to make the experience better. Currently there are two functions a user can call on the Terraform file context:

hasResource(type: string, name: string): boolean simply checks if a resource with the given type and name exists, and returns true if it does. This is useful for validation.

getResourcesOfType(type: string): Resource[] fetches every resource that is of a given type, it's a convenience filter for users that only care about resources of one type, and not all of them.

Tests were added for all new functionality added to make the purpose and intent clear of each component added or changed to support the Terraform file context.

Alternatives considered

N/A

What the reviewer should know

The changes made closely mimic what was done in #385, except we're using a MirroredVec instead of a MirroredIndexMap since we have an array of resources.

amaanq commented 3 months ago

squashing and merging