Skip to content

Commit 53d8a76

Browse files
authored
Merge pull request #25 from 0xdabbad00/use_custom_config_file
Use custom config file
2 parents 38ebb60 + 83fc67f commit 53d8a76

5 files changed

Lines changed: 132 additions & 14 deletions

File tree

README.md

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,93 @@ pip install parliament
1717

1818
# Usage
1919
```
20-
$ parliament --string '{"Version":"2012-10-17","Statement": {"Effect": "Allow","Action":["s3:GetObject"],"Resource": ["arn:aws:s3:::bucket1"]}}'
21-
INVALID - No resources match for s3:GetObject which requires a resource format of arn:*:s3:::*/* for the resource object* - {'filepath': None}
20+
cat > test.json << EOF
21+
{
22+
"Version": "2012-10-17",
23+
"Statement": {
24+
"Effect": "Allow",
25+
"Action":["s3:GetObject"],
26+
"Resource": ["arn:aws:s3:::bucket1"]
27+
}
28+
}
29+
EOF
30+
31+
parliament --file test.json
32+
```
33+
34+
This will output:
35+
```
36+
MEDIUM - No resources match for the given action - - [{'action': 's3:GetObject', 'required_format': 'arn:*:s3:::*/*'}] - {'actions': ['s3:GetObject'], 'filepath': 'test.json'}
37+
```
38+
39+
This example is showing that the action s3:GetObject requires a resource matching an object path (ie. it must have a "/" in it).
40+
41+
## Custom config file
42+
You may decide you want to change the severity of a finding, the text associated with it, or that you want to ignore certain types of findings. To support this, you can provide an override config file. First, create a test.json file:
43+
44+
```
45+
{
46+
"Version": "2012-10-17",
47+
"Id": "123",
48+
"Statement": [
49+
{
50+
"Effect": "Allow",
51+
"Action": "s3:abc",
52+
"Resource": "*"
53+
},
54+
{
55+
"Effect": "Allow",
56+
"Action": ["s3:*", "ec2:*"],
57+
"Resource": "arn:aws:s3:::test/*"
58+
}
59+
]
60+
}
61+
```
62+
63+
This will have two findings:
64+
- LOW - Unknown action - - Unknown action s3:abc
65+
- MEDIUM - No resources match for the given action
66+
67+
The second finding will be very long, because every s3 and ec2 action are expanded and most are incorrect for the S3 object path resource that is provided.
68+
69+
Now create a file `config_override.yaml` with the following contents:
70+
2271
```
72+
UNKNOWN_ACTION:
73+
severity: MEDIUM
74+
ignore_locations:
75+
filepath:
76+
- testa.json
77+
- .py
2378
24-
This example is showing that a resource specifying an S3 bucket (not an object path) was given in a policy with s3:GetObject, which requires an object path.
79+
RESOURCE_MISMATCH:
80+
ignore_locations:
81+
actions: "s3:*"
82+
```
83+
84+
Now run: `parliament --file test.json --config config_override.yaml`
85+
You will have only one output: `MEDIUM - Unknown action - - Unknown action s3:abc`
86+
87+
Notice that the severity of that finding has been changed from a `LOW` to a `MEDIUM`. Also, note that the other finding is gone, because the previous `RESOURCE_MISMATCH` finding contained an `actions` element of `["s3:*", "ec2:*"]`. The ignore logic looks for any of the values you provide in the element within `location`. This means that we are doing `if "s3:*" in str(["s3:*", "ec2:*"])`
88+
89+
Now rename `test.json` to `testa.json` and rerun the command. You will no longer have any output, because we are filtering based on the filepath for `UNKNOWN_ACTION` and filtering for any filepaths that contain `testa.json` or `.py`.
90+
91+
You can also check the exit status with `echo $?` and see the exit status is 0 when there are no findings. The exit status is set to the number of findings (ex. 10 findings results in an exit status of 10).
92+
93+
94+
95+
96+
## Using parliament as a library
97+
Parliament was meant to be used a library in other projects. A basic example follows.
98+
99+
```
100+
from parliament import analyze_policy_string
101+
102+
analyzed_policy = analyze_policy_string(policy_doc)
103+
for f in analyzed_policy.findings:
104+
print(f)
105+
```
25106

26-
See `./bin/parliament.py` for further examples.
27107

28108
# Development
29109
Setup a testing environment

parliament/__init__.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
This library is a linter for AWS IAM policies.
33
"""
4-
__version__ = "0.3.4"
4+
__version__ = "0.3.5"
55

66
import os
77
import json
@@ -19,13 +19,27 @@
1919
config = yaml.safe_load(open(config_path, "r"))
2020

2121

22+
def override_config(override_config_path):
23+
if override_config_path is None:
24+
return
25+
26+
# Load the override file
27+
override_config = yaml.safe_load(open(override_config_path, "r"))
28+
29+
# Over-write the settings
30+
for finding_type, settings in override_config.items():
31+
for setting, settting_value in settings.items():
32+
config[finding_type][setting] = settting_value
33+
34+
2235
def enhance_finding(finding):
2336
if finding.issue not in config:
2437
raise Exception("Uknown finding issue: {}".format(finding.issue))
2538
config_settings = config[finding.issue]
2639
finding.severity = config_settings["severity"]
2740
finding.title = config_settings["title"]
2841
finding.description = config_settings.get("description", "")
42+
finding.ignore_locations = config_settings.get("ignore_locations", None)
2943
return finding
3044

3145

@@ -44,12 +58,15 @@ def analyze_policy_string(policy_str, filepath=None):
4458
policy.analyze()
4559
return policy
4660

61+
4762
class UnknownPrefixException(Exception):
4863
pass
4964

65+
5066
class UnknownActionException(Exception):
5167
pass
5268

69+
5370
def is_arn_match(resource_type, arn_format, resource):
5471
"""
5572
Match the arn_format specified in the docs, with the resource
@@ -217,7 +234,9 @@ def expand_action(action, raise_exceptions=True):
217234
raise UnknownPrefixException("Unknown prefix {}".format(prefix))
218235

219236
if len(actions) == 0 and raise_exceptions:
220-
raise UnknownActionException("Unknown action {}:{}".format(prefix, unexpanded_action))
237+
raise UnknownActionException(
238+
"Unknown action {}:{}".format(prefix, unexpanded_action)
239+
)
221240

222241
return actions
223242

parliament/cli.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,27 @@
66
import sys
77
import json
88

9-
from parliament import analyze_policy_string, enhance_finding
9+
from parliament import analyze_policy_string, enhance_finding, override_config
10+
from parliament.misc import make_list
1011

1112

1213
def is_finding_filtered(finding, minimum_severity="LOW"):
1314
# Return True if the finding should not be displayed
1415
minimum_severity = minimum_severity.upper()
15-
finding = enhance_finding(finding)
1616
severity_choices = ["MUTE", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]
1717
if severity_choices.index(finding.severity) < severity_choices.index(
1818
minimum_severity
1919
):
2020
return True
21+
22+
if finding.ignore_locations:
23+
for location_type, locations_to_ignore in finding.ignore_locations.items():
24+
for location_to_ignore in make_list(locations_to_ignore):
25+
if (
26+
location_to_ignore.lower()
27+
in str(finding.location.get(location_type, "")).lower()
28+
):
29+
return True
2130
return False
2231

2332

@@ -40,7 +49,11 @@ def print_finding(finding, minimal_output=False, json_output=False):
4049
else:
4150
print(
4251
"{} - {} - {} - {} - {}".format(
43-
finding.severity, finding.title, finding.description, finding.detail, finding.location
52+
finding.severity,
53+
finding.title,
54+
finding.description,
55+
finding.detail,
56+
finding.location,
4457
)
4558
)
4659

@@ -74,6 +87,9 @@ def main():
7487
help="Minimum severity to display. Options: CRITICAL, HIGH, MEDIUM, LOW, INFO",
7588
default="LOW",
7689
)
90+
parser.add_argument(
91+
"--config", help="Custom config file for over-riding values", type=str
92+
)
7793
args = parser.parse_args()
7894

7995
if args.minimal and args.json:
@@ -149,7 +165,9 @@ def main():
149165
exit(-1)
150166

151167
filtered_findings = []
168+
override_config(args.config)
152169
for finding in findings:
170+
finding = enhance_finding(finding)
153171
if not is_finding_filtered(finding, args.minimum_severity):
154172
filtered_findings.append(finding)
155173

@@ -160,7 +178,7 @@ def main():
160178
for finding in filtered_findings:
161179
print_finding(finding, args.minimal, args.json)
162180
# There were findings, so return with a non-zero exit code
163-
exit(1)
181+
exit(len(filtered_findings))
164182

165183

166184
if __name__ == "__main__":

parliament/finding.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Finding:
1010
severity = ""
1111
title = ""
1212
description = ""
13+
ignore_locations = {}
1314

1415
def __init__(self, issue, detail, location):
1516
self.issue = issue

parliament/statement.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -710,17 +710,17 @@ def analyze_statement(self):
710710
expanded_actions.extend(expand_action(action))
711711
except UnknownActionException as e:
712712
self.add_finding(
713-
"UNKNOWN_ACTION", detail=str(e), location={"string": self.stmt}
713+
"UNKNOWN_ACTION", detail=str(e), location={"unknown_action": action, "statement": self.stmt}
714714
)
715715
return False
716716
except UnknownPrefixException as e:
717717
self.add_finding(
718-
"UNKNOWN_PREFIX", detail=str(e), location={"string": self.stmt}
718+
"UNKNOWN_PREFIX", detail=str(e), location={"statement": self.stmt}
719719
)
720720
return False
721721
except Exception as e:
722722
self.add_finding(
723-
"EXCEPTION", detail=str(e), location={"string": self.stmt}
723+
"EXCEPTION", detail=str(e), location={"statement": self.stmt}
724724
)
725725
return False
726726

@@ -846,7 +846,7 @@ def analyze_statement(self):
846846
)
847847
if actions_without_matching_resources:
848848
self.add_finding(
849-
"RESOURCE_MISMATCH", detail=actions_without_matching_resources
849+
"RESOURCE_MISMATCH", detail=actions_without_matching_resources, location={"actions": actions}
850850
)
851851

852852
# If conditions exist, it will be an element, which was previously made into a list

0 commit comments

Comments
 (0)