Skip to content

Commit

Permalink
Support pushdown of module ignores (#817)
Browse files Browse the repository at this point in the history
when the top level module has an ignore code above it, any resources in
that module which fail the check will be ignored.

This will include nested modules
  • Loading branch information
owenrumney committed Jul 5, 2021
1 parent f15dba0 commit 046dbdc
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 20 deletions.
26 changes: 26 additions & 0 deletions example/moduleWithIgnore/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#tfsec:ignore:AWS052:exp:2021-02-02
module "db" {
source = "terraform-aws-modules/rds/aws"
version = "~> 2.0"

identifier = "demodb"

engine = "mysql"
engine_version = "5.7.19"
instance_class = "db.t2.large"
allocated_storage = 5

name = "demodb"
username = "user"
password = aws_ssm_parameter.pw.value
port = "3306"

maintenance_window = "Mon:00:00-Mon:03:00"
backup_window = "03:00-06:00"
}

resource "aws_ssm_parameter" "pw" {
name = "pw"
type = "SecureString"
value = "changeme"
}
7 changes: 7 additions & 0 deletions internal/app/tfsec/block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ func (block *Block) HasModuleBlock() bool {
return block.moduleBlock != nil
}

func (block *Block) GetModuleBlock() (*Block, error) {
if block.HasModuleBlock() {
return block.moduleBlock, nil
}
return nil, fmt.Errorf("the block does not have an associated module block")
}

func (block *Block) body() *hclsyntax.Body {
return block.hclBlock.Body.(*hclsyntax.Body)
}
Expand Down
76 changes: 56 additions & 20 deletions internal/app/tfsec/scanner/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (scanner *Scanner) Scan(blocks []*block.Block) []result.Result {
results = append(results, *res)
} else if ruleResults != nil {
for _, ruleResult := range ruleResults.All() {
if scanner.includeIgnored || (!scanner.checkRangeIgnored(ruleResult.RuleID, ruleResult.Range, checkBlock.Range()) && !checkInList(ruleResult.RuleID, scanner.excludedRuleIDs)) {
if scanner.includeIgnored || (!scanner.checkRangeIgnored(ruleResult.RuleID, ruleResult.Range, checkBlock) && !checkInList(ruleResult.RuleID, scanner.excludedRuleIDs)) {
results = append(results, ruleResult)
} else {
// rule was ignored
Expand All @@ -84,18 +84,27 @@ func (scanner *Scanner) Scan(blocks []*block.Block) []result.Result {
return results
}

func (scanner *Scanner) checkRangeIgnored(id string, r block.Range, b block.Range) bool {
raw, err := ioutil.ReadFile(b.Filename)
func readLines(filename string) ([]string, error) {
raw, err := ioutil.ReadFile(filename)
if err != nil {
return false
return nil, err
}

return append([]string{""}, strings.Split(string(raw), "\n")...), nil
}

func (scanner *Scanner) checkRangeIgnored(id string, r block.Range, b *block.Block) bool {
lines, err := readLines(b.Range().Filename)
if err != nil {
debug.Log("the file containing the block could not be opened. %s", err.Error())
}
startLine := r.StartLine

ignoreAll := "tfsec:ignore:*"
ignoreCode := fmt.Sprintf("tfsec:ignore:%s", id)
lines := append([]string{""}, strings.Split(string(raw), "\n")...)
startLine := r.StartLine

foundValidIgnore := false
lineValidIgnoreFound := 0
var ignoreLine string

// include the line above the line if available
if r.StartLine-1 > 0 {
Expand All @@ -110,44 +119,71 @@ func (scanner *Scanner) checkRangeIgnored(id string, r block.Range, b block.Rang

if strings.Contains(lines[number], ignoreAll) || strings.Contains(lines[number], ignoreCode) {
foundValidIgnore = true
lineValidIgnoreFound = number
ignoreLine = lines[number]
break
}
}

// check the line above the actual resource block
if b.StartLine-1 > 0 {
line := lines[b.StartLine-1]
if b.Range().StartLine-1 > 0 {
line := lines[b.Range().StartLine-1]
if strings.Contains(line, ignoreAll) || strings.Contains(line, ignoreCode) {
foundValidIgnore = true
lineValidIgnoreFound = b.StartLine - 1
ignoreLine = line
}
}

// if nothing found yet, walk up any module references
if !foundValidIgnore {
foundValidIgnore, ignoreLine = traverseModuleTree(b, ignoreAll, ignoreCode)
}

if foundValidIgnore {
lineWithPotentialExp := lines[lineValidIgnoreFound]
expWithCode := fmt.Sprintf("%s:exp:", id)
if indexExpFound := strings.Index(lineWithPotentialExp, expWithCode); indexExpFound > 0 {
debug.Log("Expiration date found on ignore '%s'", lineWithPotentialExp)
if indexExpFound := strings.Index(ignoreLine, expWithCode); indexExpFound > 0 {
debug.Log("Expiration date found on ignore '%s'", ignoreLine)
layout := fmt.Sprintf("%s2006-01-02", expWithCode)
expDate := lineWithPotentialExp[indexExpFound : indexExpFound+len(layout)]
expDate := ignoreLine[indexExpFound : indexExpFound+len(layout)]
parsedDate, err := time.Parse(layout, expDate)

if err != nil {
// if we can't parse the date then we don't want to ignore the range
debug.Log("Unable to parse exp date in ignore: '%s'. The date format is invalid. Supported format 'exp:yyyy-mm-dd'.", lineWithPotentialExp)
debug.Log("Unable to parse exp date in ignore: '%s'. The date format is invalid. Supported format 'exp:yyyy-mm-dd'.", ignoreLine)
return false
}

currentTime := time.Now()
ignoreExpirationDateBreached := currentTime.After(parsedDate)
if ignoreExpirationDateBreached {
debug.Log("Ignore expired, check will be performed Filename: %s:%d", b.Filename, b.StartLine)
debug.Log("Ignore expired, check will be performed Filename: %s:%d", b.Range().Filename, b.Range().StartLine)
}

return !ignoreExpirationDateBreached
}
}

return foundValidIgnore
}

func traverseModuleTree(b *block.Block, ignoreAll, ignoreCode string) (bool, string) {

// check on the module
if b.HasModuleBlock() {
moduleBlock, err := b.GetModuleBlock()
if err != nil {
debug.Log("error occured trying to get the module block for [%s]. %s", b.FullName(), err.Error())
return false, ""
}
moduleLines, err := readLines(moduleBlock.Range().Filename)
if err != nil {
return false, ""
}
if moduleBlock.Range().StartLine-1 > 0 {
line := moduleLines[moduleBlock.Range().StartLine-1]
if strings.Contains(line, ignoreAll) || strings.Contains(line, ignoreCode) {
return true, line
}
}

return traverseModuleTree(moduleBlock, ignoreAll, ignoreCode)
}

return false, ""
}

0 comments on commit 046dbdc

Please sign in to comment.