Skip to content

Commit 9d021bf

Browse files
authored
Handled sentinel policy scenario for copywrite header (#119)
* Handled sentinel policy scenario * resolved comments * resolved comment * resolved comments * changed upload artifact version to fix build failure * version removed from comment
1 parent 87b93e5 commit 9d021bf

12 files changed

+357
-4
lines changed

.github/workflows/golangci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
version: latest
7575
args: release --clean --skip=publish --snapshot
7676

77-
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
77+
- uses: actions/upload-artifact@v4
7878
with:
7979
name: copywrite
8080
path: dist/*

addlicense/main.go

+40-3
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data Li
328328
return false, err
329329
}
330330

331-
line := hashBang(b)
331+
line := hashBang(b, path)
332332
if len(line) > 0 {
333333
b = b[len(line):]
334334
if line[len(line)-1] != '\n' {
@@ -365,7 +365,7 @@ func licenseHeader(path string, tmpl *template.Template, data LicenseData) ([]by
365365
lic, err = executeTemplate(tmpl, data, "/**", " * ", " */")
366366
case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".m", ".mm", ".proto", ".rs", ".swift", ".dart", ".groovy", ".v", ".sv", ".lr":
367367
lic, err = executeTemplate(tmpl, data, "", "// ", "")
368-
case ".py", ".sh", ".bash", ".zsh", ".yaml", ".yml", ".dockerfile", "dockerfile", ".rb", "gemfile", ".ru", ".tcl", ".hcl", ".tf", ".tfvars", ".nomad", ".bzl", ".pl", ".pp", ".ps1", ".psd1", ".psm1", ".txtar":
368+
case ".py", ".sh", ".bash", ".zsh", ".yaml", ".yml", ".dockerfile", "dockerfile", ".rb", "gemfile", ".ru", ".tcl", ".hcl", ".tf", ".tfvars", ".nomad", ".bzl", ".pl", ".pp", ".ps1", ".psd1", ".psm1", ".txtar", ".sentinel":
369369
lic, err = executeTemplate(tmpl, data, "", "# ", "")
370370
case ".el", ".lisp":
371371
lic, err = executeTemplate(tmpl, data, "", ";; ", "")
@@ -414,8 +414,44 @@ var head = []string{
414414
"/** @jest-environment", // Jest Environment string https://jestjs.io/docs/configuration#testenvironment-string
415415
}
416416

417-
func hashBang(b []byte) []byte {
417+
// We need to skip the top file comments in sentinel files because they are are currently used to
418+
// show policy text in UI in TFC. The patterns are created based on the comment format given
419+
// in https://developer.hashicorp.com/sentinel/docs/language/spec#comments
420+
var sentinelHeadPatterns = []string{
421+
`^#.*\n?(#.*\n?)*\n`,
422+
`^//.*\n?(//.*\n?)*\n`,
423+
`^/\*.*\n?(.*\n?)*\*/\n\n`,
424+
}
425+
426+
// matches regex patterns to extract headings to skip
427+
func matchPattern(b []byte, path string) []byte {
428+
base := strings.ToLower(filepath.Base(path))
429+
var headPatterns []string
430+
switch fileExtension(base) {
431+
case ".sentinel":
432+
headPatterns = sentinelHeadPatterns
433+
default:
434+
headPatterns = []string{}
435+
}
436+
437+
for _, v := range headPatterns {
438+
re := regexp.MustCompile(v)
439+
match := re.Find(b)
440+
if len(match) > 0 {
441+
return match
442+
}
443+
}
444+
return []byte{}
445+
}
446+
447+
func hashBang(b []byte, path string) []byte {
418448
var line []byte
449+
450+
line = matchPattern(b, path)
451+
if len(line) > 0 {
452+
return line
453+
}
454+
419455
for _, c := range b {
420456
line = append(line, c)
421457
if c == '\n' {
@@ -428,6 +464,7 @@ func hashBang(b []byte) []byte {
428464
return line
429465
}
430466
}
467+
431468
return nil
432469
}
433470

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
resource is according to CIS standards. */
3+
4+
# Copyright 2018 Google LLC
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
# Imports
19+
20+
import "tfconfig/v2" as tfconfig
21+
import "tfplan/v2" as tfplan
22+
import "tfresources" as tf
23+
import "report" as report
24+
import "collection" as collection
25+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
# resource is according to CIS standards.
3+
#
4+
# another text
5+
6+
# Copyright 2018 Google LLC
7+
#
8+
# Licensed under the Apache License, Version 2.0 (the "License");
9+
# you may not use this file except in compliance with the License.
10+
# You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
# See the License for the specific language governing permissions and
18+
# limitations under the License.
19+
20+
# Imports
21+
22+
import "tfconfig/v2" as tfconfig
23+
import "tfplan/v2" as tfplan
24+
import "tfresources" as tf
25+
import "report" as report
26+
import "collection" as collection
27+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
// resource is according to CIS standards.
3+
4+
# Copyright 2018 Google LLC
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
# Imports
19+
20+
import "tfconfig/v2" as tfconfig
21+
import "tfplan/v2" as tfplan
22+
import "tfresources" as tf
23+
import "report" as report
24+
import "collection" as collection
25+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import "tfconfig/v2" as tfconfig
16+
import "tfplan/v2" as tfplan
17+
import "tfresources" as tf
18+
import "report" as report
19+
import "collection" as collection
20+
import "collection/maps" as maps
21+
22+
# Constants
23+
24+
const = {
25+
"resource_efs_file_system": "aws_efs_file_system",
26+
"policy_name": "efs-encryption-at-rest-enabled",
27+
"kms_key_id": "kms_key_id",
28+
"constant_value": "constant_value",
29+
"encrypted": "encrypted",
30+
"encrypted_attr_violation_msg": "Attribute 'encrypted' should be true for 'aws_efs_file_system' resources. Refer to https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-1 for more details.",
31+
"kms_key_id_attr_violation_msg": "Attribute 'kms_key_id' should be non empty for 'aws_efs_file_system' resources. Refer to https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-1 for more details.",
32+
}
33+
34+
# Functions
35+
36+
build_violation_object = func(res, message) {
37+
return {
38+
"address": res.address,
39+
"module_address": res.module_address,
40+
"message": message,
41+
}
42+
}
43+
44+
# Variables
45+
46+
efs_file_systems_from_plan = tf.plan(tfplan.planned_values.resources).type(const.resource_efs_file_system).resources
47+
48+
# Filter out aws_efs_file_systems that have invalid 'encrypted' attribute
49+
non_encrypted_file_systems = collection.reject(efs_file_systems_from_plan, func(res) {
50+
encrypted_val = maps.get(res, "values.encrypted", false)
51+
return encrypted_val is true
52+
})
53+
54+
non_encrypted_file_systems_violations = map non_encrypted_file_systems as _, res {
55+
build_violation_object(res, const.encrypted_attr_violation_msg)
56+
}
57+
58+
efs_file_systems_from_configs = tf.config(tfconfig.resources).type(const.resource_efs_file_system).resources
59+
60+
# Filter out aws_efs_file_systems that have empty 'kms_key_id' attribute
61+
efs_resources_with_empty_kms_key_ids = collection.reject(efs_file_systems_from_configs, func(res) {
62+
key_path = "config.kms_key_id"
63+
return maps.get(res, key_path, false) is not false and
64+
maps.get(res, key_path + "." + const.constant_value, false) is not ""
65+
})
66+
67+
efs_resources_with_empty_kms_key_ids_violations = map efs_resources_with_empty_kms_key_ids as _, res {
68+
build_violation_object(res, const.kms_key_id_attr_violation_msg)
69+
}
70+
71+
summary = {
72+
"policy_name": const.policy_name,
73+
"violations": non_encrypted_file_systems_violations + efs_resources_with_empty_kms_key_ids_violations,
74+
}
75+
76+
# Outputs
77+
78+
print(report.generate_policy_report(summary))
79+
80+
# Rules
81+
82+
verify_non_encrypted_file_systems = rule {
83+
non_encrypted_file_systems_violations is empty
84+
}
85+
86+
verify_kms_key_referencing_file_systems = rule {
87+
efs_resources_with_empty_kms_key_ids_violations is empty
88+
}
89+
90+
main = rule {
91+
verify_non_encrypted_file_systems and verify_kms_key_referencing_file_systems
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
3+
# Copyright 2018 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Imports
18+
19+
import "tfconfig/v2" as tfconfig
20+
import "tfplan/v2" as tfplan
21+
import "tfresources" as tf
22+
import "report" as report
23+
import "collection" as collection
24+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
resource is according to CIS standards. */
3+
4+
# Imports
5+
6+
import "tfconfig/v2" as tfconfig
7+
import "tfplan/v2" as tfplan
8+
import "tfresources" as tf
9+
import "report" as report
10+
import "collection" as collection
11+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
# resource is according to CIS standards.
3+
#
4+
# another text
5+
6+
# Imports
7+
8+
import "tfconfig/v2" as tfconfig
9+
import "tfplan/v2" as tfplan
10+
import "tfresources" as tf
11+
import "report" as report
12+
import "collection" as collection
13+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This policy requires that the `require_lowercase_characters` attribute of the `aws_iam_account_password_policy`
2+
// resource is according to CIS standards.
3+
4+
# Imports
5+
6+
import "tfconfig/v2" as tfconfig
7+
import "tfplan/v2" as tfplan
8+
import "tfresources" as tf
9+
import "report" as report
10+
import "collection" as collection
11+
import "collection/maps" as maps
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import "tfconfig/v2" as tfconfig
2+
import "tfplan/v2" as tfplan
3+
import "tfresources" as tf
4+
import "report" as report
5+
import "collection" as collection
6+
import "collection/maps" as maps
7+
8+
# Constants
9+
10+
const = {
11+
"resource_efs_file_system": "aws_efs_file_system",
12+
"policy_name": "efs-encryption-at-rest-enabled",
13+
"kms_key_id": "kms_key_id",
14+
"constant_value": "constant_value",
15+
"encrypted": "encrypted",
16+
"encrypted_attr_violation_msg": "Attribute 'encrypted' should be true for 'aws_efs_file_system' resources. Refer to https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-1 for more details.",
17+
"kms_key_id_attr_violation_msg": "Attribute 'kms_key_id' should be non empty for 'aws_efs_file_system' resources. Refer to https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-1 for more details.",
18+
}
19+
20+
# Functions
21+
22+
build_violation_object = func(res, message) {
23+
return {
24+
"address": res.address,
25+
"module_address": res.module_address,
26+
"message": message,
27+
}
28+
}
29+
30+
# Variables
31+
32+
efs_file_systems_from_plan = tf.plan(tfplan.planned_values.resources).type(const.resource_efs_file_system).resources
33+
34+
# Filter out aws_efs_file_systems that have invalid 'encrypted' attribute
35+
non_encrypted_file_systems = collection.reject(efs_file_systems_from_plan, func(res) {
36+
encrypted_val = maps.get(res, "values.encrypted", false)
37+
return encrypted_val is true
38+
})
39+
40+
non_encrypted_file_systems_violations = map non_encrypted_file_systems as _, res {
41+
build_violation_object(res, const.encrypted_attr_violation_msg)
42+
}
43+
44+
efs_file_systems_from_configs = tf.config(tfconfig.resources).type(const.resource_efs_file_system).resources
45+
46+
# Filter out aws_efs_file_systems that have empty 'kms_key_id' attribute
47+
efs_resources_with_empty_kms_key_ids = collection.reject(efs_file_systems_from_configs, func(res) {
48+
key_path = "config.kms_key_id"
49+
return maps.get(res, key_path, false) is not false and
50+
maps.get(res, key_path + "." + const.constant_value, false) is not ""
51+
})
52+
53+
efs_resources_with_empty_kms_key_ids_violations = map efs_resources_with_empty_kms_key_ids as _, res {
54+
build_violation_object(res, const.kms_key_id_attr_violation_msg)
55+
}
56+
57+
summary = {
58+
"policy_name": const.policy_name,
59+
"violations": non_encrypted_file_systems_violations + efs_resources_with_empty_kms_key_ids_violations,
60+
}
61+
62+
# Outputs
63+
64+
print(report.generate_policy_report(summary))
65+
66+
# Rules
67+
68+
verify_non_encrypted_file_systems = rule {
69+
non_encrypted_file_systems_violations is empty
70+
}
71+
72+
verify_kms_key_referencing_file_systems = rule {
73+
efs_resources_with_empty_kms_key_ids_violations is empty
74+
}
75+
76+
main = rule {
77+
verify_non_encrypted_file_systems and verify_kms_key_referencing_file_systems
78+
}

0 commit comments

Comments
 (0)