Skip to content

Commit 06065eb

Browse files
authored
Bump parser version to avoid catastrophic backtracking (bridgecrewio#1677)
* Add OS-level tests * Fix example and really bump parser version * Fix parser * Bump in setup.py * Run python UT without coverage to avoid windows failure * Bumping * Update tests files to hcl2 syntax * Fix path based tests to handle windows correctly * Remove fail-fast * Move windows tests to IT instead of UT * Use bash -c * Another windows fix * Fix IT file path * Fix IT file path * Fix IT file path * prepare_data.sh supporting windows * Use bash * Log error in helm failures * Handle non-relative path in helm runner * Add logs * Skip helm windows test and update build.yml * Skip helm test for windows * Revert unnecessary changes * Merge fix * Fix UTs
1 parent f3ee7cc commit 06065eb

File tree

21 files changed

+101
-136
lines changed

21 files changed

+101
-136
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@ jobs:
1616
fail-fast: true
1717
matrix:
1818
python: [3.7, 3.8, 3.9]
19-
runs-on: ubuntu-latest
19+
os: [ubuntu-latest, macos-latest, windows-latest]
20+
runs-on: ${{ matrix.os }}
2021
steps:
2122
- uses: actions/checkout@v2
2223
- uses: actions/setup-python@v2
2324
with:
2425
python-version: ${{ matrix.python }}
25-
- uses: dschep/install-pipenv-action@v1
2626
- uses: actions/setup-node@v2
2727
- uses: azure/setup-helm@v1
28+
- uses: dschep/install-pipenv-action@v1
2829
- name: Build & install checkov package
2930
run: |
3031
pipenv --python ${{ matrix.python }}
3132
pipenv run pip install --upgrade pip==21.1.1
3233
pipenv run pip install pytest
3334
pipenv run python setup.py sdist bdist_wheel
34-
pipenv run pip install dist/checkov-*.whl
35+
bash -c 'pipenv run pip install dist/checkov-*.whl'
3536
- name: Clone Terragoat - vulnerable terraform
3637
run: git clone https://github.com/bridgecrewio/terragoat
3738
- name: Clone Cfngoat - vulnerable cloudformation
@@ -40,9 +41,10 @@ jobs:
4041
run: git clone https://github.com/madhuakula/kubernetes-goat
4142
- name: Create checkov reports
4243
run: |
43-
sleep $((RANDOM % 11))
44-
./integration_tests/prepare_data.sh ${{ matrix.python }}
44+
# Just making sure the API key tests don't run on PRs
45+
bash -c './integration_tests/prepare_data.sh ${{ matrix.os }} 3.8'
4546
env:
47+
LOG_LEVEL: INFO
4648
BC_KEY: ${{ secrets.BC_API_KEY }}
4749
- name: Run integration tests
4850
run: |

.github/workflows/pr-test.yml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@ jobs:
2222
fail-fast: true
2323
matrix:
2424
python: [3.7, 3.8, 3.9]
25-
os: [ubuntu-latest, macos-latest, windows-latest]
26-
runs-on: ${{ matrix.os }}
25+
runs-on: ubuntu-latest
2726
timeout-minutes: 30
2827
steps:
29-
- uses: actions/checkout@v1
28+
- uses: actions/checkout@v2
3029
- name: Set up Python ${{ matrix.python }}
3130
uses: actions/setup-python@v2
3231
with:
@@ -40,16 +39,15 @@ jobs:
4039
env:
4140
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4241
run: |
43-
pipenv run python -m coverage run -m pytest tests
44-
pipenv run python -m coverage report
45-
pipenv run python -m coverage html
42+
pipenv run python -m pytest tests
4643
4744
integration-tests:
4845
strategy:
4946
fail-fast: true
5047
matrix:
5148
python: [3.7, 3.8, 3.9]
52-
runs-on: ubuntu-latest
49+
os: [ubuntu-latest, macos-latest, windows-latest]
50+
runs-on: ${{ matrix.os }}
5351
steps:
5452
- uses: actions/checkout@v2
5553
- uses: actions/setup-python@v2
@@ -64,7 +62,7 @@ jobs:
6462
pipenv run pip install --upgrade pip==21.1.1
6563
pipenv run pip install pytest
6664
pipenv run python setup.py sdist bdist_wheel
67-
pipenv run pip install dist/checkov-*.whl
65+
bash -c 'pipenv run pip install dist/checkov-*.whl'
6866
- name: Clone Terragoat - vulnerable terraform
6967
run: git clone https://github.com/bridgecrewio/terragoat
7068
- name: Clone Cfngoat - vulnerable cloudformation
@@ -76,8 +74,8 @@ jobs:
7674
LOG_LEVEL: INFO
7775
BC_KEY: ${{ secrets.BC_API_KEY }}
7876
run: |
79-
sleep $((RANDOM % 11))
80-
./integration_tests/prepare_data.sh 3.8 # Just making sure the API key tests don't run on PRs
77+
# Just making sure the API key tests don't run on PRs
78+
bash -c './integration_tests/prepare_data.sh ${{ matrix.os }} 3.8'
8179
- name: Run integration tests
8280
run: |
8381
pipenv run pytest integration_tests -k 'not api_key'

Pipfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ GitPython = "*"
1414
bandit = "*"
1515
urllib3-mock = "*"
1616
jsonschema = "*"
17+
atomicwrites = "*"
1718

1819
[packages]
1920
#

Pipfile.lock

Lines changed: 18 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

checkov/helm/runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ def run(self, root_folder, external_checks_dir=None, files=None, runner_filter=R
191191
report.skipped_checks += chart_results.skipped_checks
192192
report.resources.update(chart_results.resources)
193193

194-
except:
194+
except Exception as e:
195+
logging.warning(e, stack_info=True)
195196
with tempfile.TemporaryDirectory() as save_error_dir:
196197
logging.debug(
197198
f"Error running k8s scan on {chart_meta['name']}. Scan dir: {target_dir}. Saved context dir: {save_error_dir}")

checkov/terraform/parser.py

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import sys
99
from copy import deepcopy
1010
from json import dumps, loads, JSONEncoder
11-
from multiprocessing.connection import Connection
1211
from pathlib import Path
1312
from typing import Optional, Dict, Mapping, Set, Tuple, Callable, Any, List
1413

@@ -652,63 +651,22 @@ def _load_or_die_quietly(file: os.PathLike, parsing_errors: Dict,
652651
try:
653652
logging.debug(f"Parsing {file_path}")
654653

655-
if file_name.endswith(".json"):
656-
with open(file_name, "r") as f:
654+
with open(file_path, "r") as f:
655+
if file_name.endswith(".json"):
657656
return json.load(f)
658-
else:
659-
raw_data = _hcl2_load_with_timeout(file_path)
660-
non_malformed_definitions = validate_malformed_definitions(raw_data)
661-
if clean_definitions:
662-
return clean_bad_definitions(non_malformed_definitions)
663657
else:
664-
return non_malformed_definitions
665-
658+
raw_data = hcl2.load(f)
659+
non_malformed_definitions = validate_malformed_definitions(raw_data)
660+
if clean_definitions:
661+
return clean_bad_definitions(non_malformed_definitions)
662+
else:
663+
return non_malformed_definitions
666664
except Exception as e:
667665
logging.debug(f'failed while parsing file {file_path}', exc_info=e)
668666
parsing_errors[file_path] = e
669667
return None
670668

671669

672-
def _hcl2_load_with_timeout(filename: str) -> Dict:
673-
# Start bar as a process
674-
raw_data = None
675-
reader, writer = multiprocessing.Pipe(duplex=False) # used by the child process to return its result
676-
677-
# certain Python and OS versions (for sure 3.8/3.9 + Mac) have issues with 'multiprocessing.Process'
678-
# see: https://github.com/pytorch/pytorch/issues/36515
679-
# https://bugs.python.org/issue33725
680-
# "Fork" is the correct approach on 3.8+ on Mac, and should be compatible with *nix, and generally 3.7+
681-
# It is faster to open processes, and therefore we prefer it. However, it is not supported on Windows
682-
if sys.platform == 'win32':
683-
p = multiprocessing.Process(target=_hcl2_load, args=(writer, filename))
684-
else:
685-
p = multiprocessing.get_context("fork").Process(target=_hcl2_load, args=(writer, filename))
686-
p.start()
687-
688-
# Wait until the file is parsed, up to 60 seconds, and fetch the raw data
689-
is_input_available = reader.poll(timeout=60)
690-
if is_input_available:
691-
raw_data = reader.recv()
692-
693-
# Resources cleanup
694-
if p.is_alive():
695-
p.terminate()
696-
writer.close()
697-
698-
return raw_data
699-
700-
701-
def _hcl2_load(writer: Connection, filename: str) -> None:
702-
with open(filename, 'r') as f:
703-
try:
704-
raw_data = hcl2.load(f)
705-
writer.send(raw_data)
706-
except Exception as e:
707-
logging.error(f'Failed to parse file {f.name}. Error:')
708-
logging.error(e, exc_info=True)
709-
writer.send(None)
710-
711-
712670
def _is_valid_block(block):
713671
if not isinstance(block, dict):
714672
return True

integration_tests/prepare_data.sh

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
#!/bin/bash
22

3-
pipenv run checkov -s --framework terraform -d terragoat/terraform/ -o json > checkov_report_terragoat.json
4-
pipenv run checkov -s --framework terraform -d terragoat/terraform/ -o junitxml > checkov_report_terragoat.xml
5-
pipenv run checkov -s -d cfngoat/ -o json --external-checks-dir ./checkov/cloudformation/checks/graph_checks/aws > checkov_report_cfngoat.json
6-
pipenv run checkov -s -d kubernetes-goat/ --framework kubernetes -o json > checkov_report_kubernetes-goat.json
7-
pipenv run checkov -s -d kubernetes-goat/ --framework helm -o json > checkov_report_kubernetes-goat-helm.json
8-
pipenv run checkov -s --framework terraform --skip-check CKV_AWS_33,CKV_AWS_41 -d terragoat/terraform/ -o json > checkov_report_terragoat_with_skip.json
9-
pipenv run checkov -s -d cfngoat/ -o json --quiet > checkov_report_cfngoat_quiet.json
10-
pipenv run checkov -s -d terragoat/terraform/ --config-file integration_tests/example_config_files/config.yaml -o json > checkov_config_report_terragoat.json
11-
if [[ "$1" == "3.7" ]]
3+
if [[ "$1" == "windows-latest" ]]
4+
then
5+
pipenv run checkov -s --framework terraform -d terragoat\\terraform\\ -o json > checkov_report_terragoat.json
6+
pipenv run checkov -s --framework terraform -d terragoat\\terraform\\ -o junitxml > checkov_report_terragoat.xml
7+
pipenv run checkov -s -d cfngoat\\ -o json --external-checks-dir .\\checkov\\cloudformation\\checks\\graph_checks\\aws > checkov_report_cfngoat.json
8+
pipenv run checkov -s -d kubernetes-goat\\ --framework kubernetes -o json > checkov_report_kubernetes-goat.json
9+
# LOG_LEVEL=DEBUG pipenv run checkov -s -d kubernetes-goat\\ --framework helm -o json > checkov_report_kubernetes-goat-helm.json
10+
pipenv run checkov -s --framework terraform --skip-check CKV_AWS_33,CKV_AWS_41 -d terragoat\\terraform\\ -o json > checkov_report_terragoat_with_skip.json
11+
pipenv run checkov -s -d cfngoat\\ -o json --quiet > checkov_report_cfngoat_quiet.json
12+
pipenv run checkov -s -d terragoat\\terraform\\ --config-file integration_tests\\example_config_files\\config.yaml -o json > checkov_config_report_terragoat.json
13+
else
14+
pipenv run checkov -s --framework terraform -d terragoat/terraform/ -o json > checkov_report_terragoat.json
15+
pipenv run checkov -s --framework terraform -d terragoat/terraform/ -o junitxml > checkov_report_terragoat.xml
16+
pipenv run checkov -s -d cfngoat/ -o json --external-checks-dir ./checkov/cloudformation/checks/graph_checks/aws > checkov_report_cfngoat.json
17+
pipenv run checkov -s -d kubernetes-goat/ --framework kubernetes -o json > checkov_report_kubernetes-goat.json
18+
pipenv run checkov -s -d kubernetes-goat/ --framework helm -o json > checkov_report_kubernetes-goat-helm.json
19+
pipenv run checkov -s --framework terraform --skip-check CKV_AWS_33,CKV_AWS_41 -d terragoat/terraform/ -o json > checkov_report_terragoat_with_skip.json
20+
pipenv run checkov -s -d cfngoat/ -o json --quiet > checkov_report_cfngoat_quiet.json
21+
pipenv run checkov -s -d terragoat/terraform/ --config-file integration_tests/example_config_files/config.yaml -o json > checkov_config_report_terragoat.json
22+
fi
23+
24+
if [[ "$2" == "3.7" ]]
1225
then
1326
pipenv run checkov -s -f terragoat/terraform/aws/s3.tf --bc-api-key $BC_KEY > checkov_report_s3_singlefile_api_key_terragoat.txt
1427
pipenv run checkov -s -d terragoat/terraform/azure/ --bc-api-key $BC_KEY > checkov_report_azuredir_api_key_terragoat.txt

integration_tests/test_checkov_cli_integration_report.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
class TestCheckovJsonReport(unittest.TestCase):
99

1010
def test_terragoat_report_dir_api_key(self):
11-
report_path = current_dir + "/../checkov_report_azuredir_api_key_terragoat.txt"
11+
report_path = os.path.join(current_dir, '..', 'checkov_report_azuredir_api_key_terragoat.txt')
1212
self.validate_report(os.path.abspath(report_path))
1313

1414
def test_terragoat_report_file_api_key(self):
15-
report_path = current_dir + "/../checkov_report_s3_singlefile_api_key_terragoat.txt"
15+
report_path = os.path.join(current_dir, '..', 'checkov_report_s3_singlefile_api_key_terragoat.txt')
1616
self.validate_report(os.path.abspath(report_path))
1717

1818
def validate_report(self, report_path):

integration_tests/test_checkov_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_terragoat_report(self):
1212
# checkov -d path/to/terragoat --config-file \
1313
# path/to/checkov/integration_tests/example_config_files/config.yaml \
1414
# > path/to/checkov/checkov_config_report_terragoat.json
15-
report_path = current_dir + "/../checkov_config_report_terragoat.json"
15+
report_path = os.path.join(os.path.dirname(current_dir), "checkov_config_report_terragoat.json")
1616
with open(report_path) as json_file:
1717
data = json.load(json_file)
1818
self.assertEqual(data["summary"]["parsing_errors"], 0,

integration_tests/test_checkov_json_report.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import itertools
22
import json
33
import os
4+
import sys
45
import unittest
56

67
current_dir = os.path.dirname(os.path.realpath(__file__))
@@ -9,24 +10,25 @@
910
class TestCheckovJsonReport(unittest.TestCase):
1011

1112
def test_terragoat_report(self):
12-
report_path = current_dir + "/../checkov_report_terragoat.json"
13+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_terragoat.json')
1314
self.validate_report(os.path.abspath(report_path))
1415

1516
def test_cfngoat_report(self):
16-
report_path = current_dir + "/../checkov_report_cfngoat.json"
17+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_cfngoat.json')
1718
self.validate_report(os.path.abspath(report_path))
1819
self.validate_check_in_report(report_path, "CKV2_AWS_26")
1920

2021
def test_k8goat_report(self):
21-
report_path = current_dir + "/../checkov_report_kubernetes-goat.json"
22+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_kubernetes-goat.json')
2223
self.validate_report(os.path.abspath(report_path))
2324

2425
def test_k8goat_report(self):
25-
report_path = current_dir + "/../checkov_report_kubernetes-goat-helm.json"
26-
self.validate_report(os.path.abspath(report_path))
26+
if not sys.platform.startswith('win'):
27+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_kubernetes-goat-helm.json')
28+
self.validate_report(os.path.abspath(report_path))
2729

2830
def test_checkov_report_terragoat_with_skip(self):
29-
report_path = current_dir + "/../checkov_report_terragoat_with_skip.json"
31+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_terragoat_with_skip.json')
3032
checkov2_graph_findings = 0
3133
with open(report_path) as json_file:
3234
data = json.load(json_file)
@@ -53,7 +55,7 @@ def validate_report_not_empty(self, report):
5355
f"expecting more than 1 failed checks, got: {report['summary']['failed']}")
5456

5557
def validate_json_quiet(self):
56-
report_path = current_dir + "/../checkov_report_cfngoat_quiet.json"
58+
report_path = os.path.join(os.path.dirname(current_dir), 'checkov_report_cfngoat_quiet.json')
5759
with open(report_path) as json_file:
5860
data = json.load(json_file)
5961
self.assertTrue(data["results"]["failed_checks"])

0 commit comments

Comments
 (0)