Skip to content

Commit 297a27c

Browse files
committed
prototype
1 parent 04f1d0f commit 297a27c

12 files changed

Lines changed: 2510 additions & 7 deletions

File tree

secomlint/secomlint/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
__version__ = "0.1.0"
88
__author__ = 'Sofia Reis'
9-
__credits__ = 'SecurityAware Project'
9+
__credits__ = 'SecurityAware Project'

secomlint/secomlint/__main__.py

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,122 @@
11
import sys
2-
32
import click
43

4+
from secomlint.message import Message
5+
from secomlint.config import Config
6+
from secomlint.ruler import Ruler
7+
from secomlint.section import Body
8+
9+
10+
def compliance_score(ruler, warnings):
11+
no_rules = len(ruler.rules)
12+
rules_not_in_compliance = sum(
13+
[warning.result for warning in warnings])
14+
return ((no_rules - rules_not_in_compliance) / no_rules) * 100
15+
16+
17+
def get_symbol(result, wtype, sep=" "):
18+
if result == 0:
19+
return f"✅{sep}"
20+
if wtype == 1:
21+
return f"❌{sep}"
22+
else:
23+
return f"🟡{sep}"
24+
25+
26+
def print_summary(ruler, warnings, alerts, problems, print_score=False):
27+
color = 'green' if alerts == 0 and problems == 0 else 'yellow'
28+
summary = f"\nfound {problems} problem(s), {alerts} warning(s);"
29+
if print_score:
30+
score = compliance_score(ruler, warnings)
31+
secom_link = f"[\u001b]8;;https://tqrg.github.io/secom\u001b\\SECOM\u001b]8;;\u001b\\]"
32+
click.echo(
33+
click.style(
34+
(summary
35+
+ f" 🎯 Commit message is {score:.2f}% in compliance with {secom_link} convention."),
36+
fg=color,
37+
bold=True))
38+
else:
39+
click.echo(
40+
click.style(summary, fg=color, bold=True))
41+
42+
43+
def print_body_analysis(message):
44+
body_section = [
45+
section for section in message.sections if type(section) == Body]
46+
if len(body_section) == 1:
47+
secwords, count = [], 0
48+
for entity in body_section[0].entities:
49+
entity_list = list(entity)
50+
if entity_list[1] == 'SECWORD':
51+
secwords.append(entity_list[0])
52+
count += 1
53+
if secwords:
54+
click.echo(
55+
"""👍 Good to go! Extractor found the following security related words in the message's body:""")
56+
for word in secwords:
57+
click.echo(click.style(f" - {word}", fg="green"))
58+
else:
59+
click.echo(
60+
"""🧐 The message's body is not informative enough. Try improving the message's body by adding more security related words!""")
61+
else:
62+
click.echo(
63+
"""❌ The message's body is missing! Don't forget the what, why and how structure.""")
64+
65+
566
@click.command()
6-
def main():
7-
"""Simple program that greets NAME for a total of COUNT times."""
8-
commit_msg = [line for line in sys.stdin]
9-
print(''.join(commit_msg))
67+
@click.option("--no-compliance", is_flag=True, default=False, help="Show missing compliance.")
68+
@click.option("--is-body-informative", is_flag=True, default=False, help="Checks body for security information.")
69+
@click.option("--score", is_flag=True, default=False, help="Show compliance score.")
70+
@click.option("--config", help="Rule configuration file path name.")
71+
def main(no_compliance, is_body_informative, score, config):
72+
"""Linter to check compliance against SECOM (https://tqrg.github.io/secom/)."""
73+
if not sys.stdin.isatty():
74+
commit_msg = [line for line in sys.stdin]
75+
if commit_msg:
76+
message = Message(commit_msg)
77+
message.get_sections()
78+
if len(message.text) > 0:
79+
config_ = Config(path=config)
80+
else:
81+
# TODO: raise error saying it couldn't
82+
# get sections or msg is empty
83+
return
84+
85+
ruler = Ruler(config_)
86+
warnings = []
87+
for section in message.sections:
88+
warnings += ruler.compliance(section)
89+
90+
alerts, problems = 0, 0
91+
92+
# TODO: order it by (result == 1, type == 1),
93+
# (result == 1, type == 0), (result == 0)
94+
for warning in warnings:
95+
if warning.result == 1 and warning.type == 1:
96+
click.echo(get_symbol(warning.result, warning.type)
97+
+ warning.message + ' ' +
98+
click.style(warning.link, fg="blue"))
99+
problems += 1
100+
for warning in warnings:
101+
if warning.result == 1 and warning.type == 0:
102+
click.echo(get_symbol(warning.result, warning.type)
103+
+ warning.message + ' ' +
104+
click.style(warning.link, fg="blue"))
105+
alerts += 1
106+
if not no_compliance:
107+
for warning in warnings:
108+
if warning.result == 0:
109+
click.echo(get_symbol(warning.result, warning.type)
110+
+ warning.message + ' ' +
111+
click.style(warning.link, fg="blue"))
112+
113+
print_summary(ruler, warnings, alerts, problems, print_score=score)
114+
115+
if is_body_informative:
116+
print_body_analysis(message)
117+
118+
return
10119

11120

12121
if __name__ == '__main__':
13-
main()
122+
main()

secomlint/secomlint/config.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
3+
from secomlint.utils import read_config
4+
5+
6+
class Config:
7+
def __init__(self, path=None) -> None:
8+
self.config_path = f"{os.path.dirname(os.path.abspath(__file__))}/config/rules.yml"
9+
self.default_rules = read_config(self.config_path)
10+
if path:
11+
self.new_rules = read_config(path)
12+
if self.new_rules:
13+
for rule in self.new_rules:
14+
for element in self.new_rules[rule]:
15+
self.default_rules[rule][element] = self.new_rules[rule][element]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
header_max_length:
2+
active: true
3+
type: 0
4+
value: 50
5+
header_is_not_empty:
6+
active: true
7+
type: 1
8+
value: 0
9+
header_starts_with_type:
10+
active: true
11+
type: 1
12+
value: 'vuln-fix'
13+
header_ends_with_vuln_id:
14+
active: true
15+
type: 0
16+
body_max_length:
17+
active: true
18+
type: 0
19+
value: 75
20+
body_is_not_empty:
21+
active: true
22+
type: 1
23+
value: 0
24+
body_has_three_paragraphs:
25+
active: true
26+
type: 1
27+
metadata_has_weakness:
28+
active: true
29+
type: 1
30+
metadata_has_severity:
31+
active: true
32+
type: 1
33+
metadata_has_detection:
34+
active: true
35+
type: 1
36+
metadata_has_report:
37+
active: true
38+
type: 1
39+
metadata_has_cvss:
40+
active: true
41+
type: 1
42+
metadata_has_introduced_in:
43+
active: true
44+
type: 1
45+
contact_has_reported_by:
46+
active: true
47+
type: 1
48+
value: 'reported-by'
49+
contact_has_signed_off_by:
50+
active: true
51+
type: 1
52+
value: 'signed-off-by'
53+
contact_has_co_authored_by:
54+
active: true
55+
type: 1
56+
value: 'co-authored-by'
57+
bugtracker_has_reference:
58+
active: true
59+
type: 1

0 commit comments

Comments
 (0)