offlinesec-client 1.0.30__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/PKG-INFO +4 -1
  2. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/README.md +3 -0
  3. offlinesec_client-1.1.0/offlinesec_client/__init__.py +1 -0
  4. offlinesec_client-1.1.0/offlinesec_client/abap_system.py +101 -0
  5. offlinesec_client-1.1.0/offlinesec_client/bo_system.py +31 -0
  6. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/func.py +30 -1
  7. offlinesec_client-1.1.0/offlinesec_client/java_system.py +56 -0
  8. offlinesec_client-1.1.0/offlinesec_client/multi_systems.py +50 -0
  9. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/req_java_notes.py +1 -1
  10. offlinesec_client-1.1.0/offlinesec_client/req_patch_day.py +64 -0
  11. offlinesec_client-1.1.0/offlinesec_client/req_sec_notes.py +63 -0
  12. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/rsparam.py +3 -2
  13. offlinesec_client-1.1.0/offlinesec_client/sap_system.py +69 -0
  14. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/PKG-INFO +4 -1
  15. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/SOURCES.txt +7 -0
  16. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/entry_points.txt +2 -0
  17. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/requires.txt +3 -0
  18. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/setup.py +2 -0
  19. offlinesec_client-1.0.30/offlinesec_client/__init__.py +0 -1
  20. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/__main__.py +0 -0
  21. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/agr_1251.py +0 -0
  22. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/config.py +0 -0
  23. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/const.py +0 -0
  24. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/cwbntcust.py +0 -0
  25. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/get_reports.py +0 -0
  26. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/req_bo_notes.py +0 -0
  27. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/req_notes_report.py +0 -0
  28. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/req_param_report.py +0 -0
  29. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/req_roles_report.py +0 -0
  30. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/resolve_report.py +0 -0
  31. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client/sap_gui.py +0 -0
  32. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/dependency_links.txt +0 -0
  33. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/offlinesec_client.egg-info/top_level.txt +0 -0
  34. {offlinesec_client-1.0.30 → offlinesec_client-1.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: offlinesec_client
3
- Version: 1.0.30
3
+ Version: 1.1.0
4
4
  Summary: Offline Security Client
5
5
  Home-page: https://offlinesec.com
6
6
  Author: Offline Security
@@ -88,6 +88,9 @@ offlinesec_get_reports
88
88
  * Our knowledge base is constantly updated and contain all SAP security notes released in 2015-2023. You can find the date of last loaded SAP Security Note in your report.
89
89
  * since version 1.0.29 SAP Business Object systems are supported
90
90
  * since version 1.0.30 SAP JAVA systems are supported
91
+ * since version 1.1.0 Offlinsec tool supports multi-system scan
92
+ * since version 1.1.0 Offlinesec tool supports last patch day scan
93
+ * since version 1.1.2 the API to integrate with SIEM or VM is available in Offlinesec tool
91
94
 
92
95
  2. Profile Parameters/Compliance Analysis (SAP Security Baseline Checks)
93
96
  (Available since version 1.0.12)
@@ -77,6 +77,9 @@ offlinesec_get_reports
77
77
  * Our knowledge base is constantly updated and contain all SAP security notes released in 2015-2023. You can find the date of last loaded SAP Security Note in your report.
78
78
  * since version 1.0.29 SAP Business Object systems are supported
79
79
  * since version 1.0.30 SAP JAVA systems are supported
80
+ * since version 1.1.0 Offlinsec tool supports multi-system scan
81
+ * since version 1.1.0 Offlinesec tool supports last patch day scan
82
+ * since version 1.1.2 the API to integrate with SIEM or VM is available in Offlinesec tool
80
83
 
81
84
  2. Profile Parameters/Compliance Analysis (SAP Security Baseline Checks)
82
85
  (Available since version 1.0.12)
@@ -0,0 +1 @@
1
+ __version__ = "1.1.0"
@@ -0,0 +1,101 @@
1
+ from offlinesec_client.sap_system import SAPSystem
2
+ from offlinesec_client.cwbntcust import Cwbntcust
3
+ import os
4
+ import re
5
+
6
+ ABAP = "ABAP"
7
+
8
+
9
+ class ABAPSystem (SAPSystem):
10
+ def __init__(self, **args):
11
+ super().__init__(args["name"])
12
+ self.type = ABAP
13
+ root_dir = args["root_dir"] if "root_dir" in args else None
14
+
15
+ self.kernel_version = ABAPSystem.check_kernel_version(args["krnl_version"]) if "krnl_version" in args.keys() \
16
+ else ""
17
+ self.kernel_patch = ABAPSystem.check_kernel_patch(args["krnl_patch"]) if "krnl_patch" in args.keys() else ""
18
+ self.softs = ABAPSystem.parse_softs_file(args["softs"], root_dir) if "softs" in args.keys() else list()
19
+ if self.kernel_version == "" or len(self.softs) == 0:
20
+ raise ValueError("[ERROR] System '{}' you must specify 'soft' or 'krnl_version' keys"
21
+ .format(self.system_name))
22
+ self.cwbntcust = ABAPSystem.parse_cwbntcust_file(args["cwbntcust"], root_dir, self.system_name) \
23
+ if "cwbntcust" in args.keys() else list()
24
+ self.exclude = ABAPSystem.parse_exclude_file(args["exclude"], root_dir, self.system_name) \
25
+ if "exclude" in args.keys() else list()
26
+
27
+ @staticmethod
28
+ def check_kernel_version(kernel_version):
29
+ kernel_version = str(kernel_version).replace(',', '')
30
+ kernel_version = kernel_version.replace('.', '')
31
+ try:
32
+ return int(kernel_version)
33
+ except ValueError:
34
+ raise ValueError("Kernel Version must be numeric. For example: 7.53 or 753.")
35
+
36
+ @staticmethod
37
+ def check_kernel_patch(kernel_patch):
38
+ kernel_patch = str(kernel_patch).replace(',', '')
39
+ kernel_patch = kernel_patch.replace('.', '')
40
+ try:
41
+ return int(kernel_patch)
42
+ except ValueError:
43
+ raise ValueError("Kernel Version must be numeric. For example: 1100.")
44
+
45
+ @staticmethod
46
+ def parse_softs_file(softs_file, root_dir):
47
+ if root_dir:
48
+ path = os.path.join(root_dir, softs_file)
49
+ else:
50
+ path = softs_file
51
+ if not os.path.exists(path):
52
+ raise FileNotFoundError("File %s not found" % (path,))
53
+
54
+ if not softs_file.upper().endswith(".TXT"):
55
+ raise ValueError("File {} has wrong extension. Only TXT files supported".format(softs_file))
56
+
57
+ softs = list()
58
+ with open(path, 'r', encoding="utf-8") as f:
59
+ num = 0
60
+ for line in f:
61
+ num += 1
62
+ line = line.strip('\r\n').strip()
63
+ line = re.sub(' +', ' ', line).split()
64
+ if len(line) > 4:
65
+ soft = line[0]
66
+ soft = soft.encode("utf-8").decode("utf-8-sig")
67
+ # print(soft.decode('utf-8'))
68
+ version = line[1]
69
+ pkg_num = line[2].lstrip("0")
70
+ if pkg_num == "":
71
+ pkg_num = "0"
72
+ package = line[3]
73
+ softs.append((soft, version, pkg_num, package))
74
+ else:
75
+ print("[ERROR] File %s Line %s: %s" % (softs_file, str(num), " ".join(line)))
76
+
77
+ if not len(softs):
78
+ raise ValueError("File {} has wrong format".format(softs_file))
79
+ return softs
80
+
81
+ @staticmethod
82
+ def parse_cwbntcust_file(cwbntcust_file, root_dir, system_name):
83
+ if root_dir:
84
+ path = os.path.join(root_dir, cwbntcust_file)
85
+ else:
86
+ path = cwbntcust_file
87
+ if not os.path.exists(path):
88
+ raise FileNotFoundError("File %s not found" % (cwbntcust_file,))
89
+
90
+ if not (cwbntcust_file.upper().endswith(".TXT") or cwbntcust_file.upper().endswith(".CSV")):
91
+ raise ValueError("File {} has wrong extension. Only TXT or CSV files supported".format(cwbntcust_file))
92
+
93
+ tbl = Cwbntcust(path)
94
+ notes = tbl.read_file()
95
+
96
+ if not len(notes):
97
+ print("[WARNING] System '{}' File {} has wrong format or doesn't contain completely implemented notes"
98
+ .format(system_name, cwbntcust_file,))
99
+
100
+ return notes
101
+
@@ -0,0 +1,31 @@
1
+ from offlinesec_client.sap_system import SimpleSystem
2
+
3
+
4
+ BO = "BO"
5
+
6
+
7
+ class BOSystem (SimpleSystem):
8
+ def __init__(self, **args):
9
+ root_dir = args["root_dir"] if "root_dir" in args else None
10
+ if "version" in args.keys():
11
+ super().__init__(args["name"], version=BOSystem.check_version(args["version"], args["name"]))
12
+ else:
13
+ raise ValueError("You must specify 'version' key")
14
+ self.type = BO
15
+ self.exclude = BOSystem.parse_exclude_file(args["exclude"], root_dir, self.system_name) \
16
+ if "exclude" in args.keys() else list()
17
+
18
+ @staticmethod
19
+ def check_version(v, system_name):
20
+ splitted_ver = str(v).strip('"').split(".")
21
+ if not len(splitted_ver) == 4:
22
+ raise ValueError("Bad SAP BO version format '{}'. Expected format: 14.1.4.1655".format(str(v)))
23
+
24
+ if not int(splitted_ver[0]) == 14:
25
+ raise ValueError("Bad SAP BO version format '{}'. Expected format: 14.1.4.1655".format(str(v)))
26
+ try:
27
+ for item in splitted_ver:
28
+ s = int(item)
29
+ except:
30
+ raise ValueError(" Bad SAP BO version format '{}'. Expected format: 14.1.4.1655".format(str(v)))
31
+ return v
@@ -1,7 +1,10 @@
1
1
  from .config import config
2
2
  import socket
3
3
  import argparse
4
+ import json
4
5
  import os
6
+ import requests
7
+ from offlinesec_client.const import ERR_MESSAGE
5
8
  from offlinesec_client.cwbntcust import Cwbntcust
6
9
  from offlinesec_client.const import APIKEY, CLIENT_ID, INST_DATE, ACTION, SYSTEM_NAME, CONNECTION_STR, CWBNTCUST,\
7
10
  KRNL_PL, KRNL_VER, VAR
@@ -109,4 +112,30 @@ def check_num_param(s, title="Argument"):
109
112
  num = int(s)
110
113
  except:
111
114
  raise argparse.ArgumentTypeError("%s must be numeric" % (title,))
112
- return num
115
+ return num
116
+
117
+
118
+ def send_to_server(data, url, extras={}):
119
+ url = get_connection_str(url)
120
+
121
+ send_data = get_base_json()
122
+ send_data["systems"] = [item.to_dict() for item in data]
123
+ if len(extras):
124
+ send_data.update(extras)
125
+
126
+ files = {'json': ('description', json.dumps(send_data), 'application/json')}
127
+
128
+ r = requests.post(url, files=files)
129
+ if r.content:
130
+ try:
131
+ response = json.loads(r.content)
132
+ if ERR_MESSAGE in response:
133
+ if response[ERR_MESSAGE].startswith("The data successfully"):
134
+ print(" * " + response[ERR_MESSAGE])
135
+ else:
136
+ print("[ERROR] " + response[ERR_MESSAGE])
137
+ return
138
+ except:
139
+ pass
140
+
141
+ print("[ERROR] No response from the Offline Security server. Please try later")
@@ -0,0 +1,56 @@
1
+ from offlinesec_client.sap_system import SAPSystem
2
+ import os
3
+
4
+ JAVA = "JAVA"
5
+ CSV_COLUMNS = ["Version", "Vendor", "Name", "Location"]
6
+
7
+
8
+ class JAVASystem (SAPSystem):
9
+ def __init__(self, **args):
10
+ super().__init__(args["name"])
11
+ self.type = JAVA
12
+ root_dir = args["root_dir"] if "root_dir" in args else None
13
+
14
+ self.softs = JAVASystem.parse_softs_file(args["softs"], root_dir) if "softs" in args.keys() else list()
15
+ if len(self.softs) == 0:
16
+ raise ValueError("[ERROR] System '{}' you must specify 'soft' key"
17
+ .format(self.system_name))
18
+ self.exclude = JAVASystem.parse_exclude_file(args["exclude"], root_dir, self.system_name) \
19
+ if "exclude" in args.keys() else list()
20
+
21
+ @staticmethod
22
+ def parse_softs_file(softs_file, root_dir):
23
+ if root_dir:
24
+ path = os.path.join(root_dir, softs_file)
25
+ else:
26
+ path = softs_file
27
+ if not os.path.exists(path):
28
+ raise FileNotFoundError("File %s not found" % (path,))
29
+ if not (softs_file.upper().endswith(".TXT") or softs_file.upper().endswith(".CSV")):
30
+ raise ValueError("File {} has wrong extension. Only TXT or CSV files supported".format(softs_file))
31
+
32
+ out_softs = list()
33
+
34
+ with open(path, 'r') as f:
35
+ columns = dict()
36
+ first_line = True
37
+ for line in f:
38
+ line = line.strip('\r\n')
39
+ if len(line) == 0:
40
+ continue
41
+ line = line.split(',')
42
+ if first_line:
43
+ first_line = False
44
+ for column1 in CSV_COLUMNS:
45
+ for pos, column2 in enumerate(line):
46
+ if column1.lower() == column2.lower().strip():
47
+ columns[column1] = pos
48
+ else:
49
+ name = line[columns[CSV_COLUMNS[2]]]
50
+ ver = line[columns[CSV_COLUMNS[0]]]
51
+ out_softs.append((name, ver))
52
+
53
+ if not len(out_softs):
54
+ raise ValueError("File {} has wrong format".format(softs_file))
55
+ return out_softs
56
+
@@ -0,0 +1,50 @@
1
+ import yaml
2
+ from offlinesec_client.const import FILE
3
+ from offlinesec_client.abap_system import ABAPSystem
4
+ from offlinesec_client.java_system import JAVASystem
5
+ from offlinesec_client.bo_system import BOSystem
6
+
7
+
8
+ def process_yaml_file(args):
9
+ file_name =args[FILE]
10
+ error_list = list()
11
+ system_list = list()
12
+
13
+ with open(file_name, 'r', encoding="utf-8") as f:
14
+ file_content = yaml.safe_load(f)
15
+
16
+ root_dir = file_content["root_dir"] if "root_dir" in file_content else ""
17
+ if not "sap_systems" in file_content:
18
+ return
19
+
20
+ for num, system in enumerate(file_content["sap_systems"]):
21
+ if "name" not in system.keys():
22
+ system["name"] = "System {}".format(str(num+1))
23
+ system_type = system["type"] if "type" in system.keys() else ""
24
+ if root_dir != "":
25
+ system["root_dir"] = root_dir
26
+ if system_type == "":
27
+ err = "[ERROR] System '{}' doesn't contain key 'type'".format(system["name"])
28
+ print(err)
29
+ continue
30
+
31
+ try:
32
+ if system_type.upper().strip() == "ABAP":
33
+ new_item = ABAPSystem(**system)
34
+ elif system_type.upper().strip() == "JAVA":
35
+ new_item = JAVASystem(**system)
36
+ elif system_type.upper().strip() == "BO":
37
+ new_item = BOSystem(**system)
38
+ else:
39
+ err = "[ERROR] System '{}' Unknown system type '{}'".format(system["name"], system_type)
40
+ print(err)
41
+ continue
42
+ except (FileNotFoundError, ValueError) as error:
43
+ err = "[ERROR] System '{}' {}".format(system["name"], str(error))
44
+ print(err)
45
+ continue
46
+ else:
47
+ if new_item:
48
+ system_list.append(new_item)
49
+
50
+ return system_list
@@ -32,7 +32,7 @@ def prepare_exclusions(excl_list, excl_file):
32
32
  outdict = list()
33
33
 
34
34
  if excl_file:
35
- with open(excl_file,'r') as f:
35
+ with open(excl_file, 'r') as f:
36
36
  for line in f:
37
37
  line = line.strip("\r\n")
38
38
  line = line.split(",")
@@ -0,0 +1,64 @@
1
+ import argparse
2
+ import yaml
3
+ import sys
4
+ import time
5
+ import os
6
+ import offlinesec_client.func
7
+ from offlinesec_client.const import FILE
8
+ from offlinesec_client.multi_systems import process_yaml_file
9
+
10
+
11
+ # SUPPORTED_SYSTEMS = ["ABAP", "JAVA", "BO", "HANA"]
12
+ UPLOAD_URL = "/sec-notes"
13
+
14
+
15
+ def check_file_arg(s):
16
+ res = offlinesec_client.func.check_file_arg(s, ['yaml'], 200000)
17
+ with open(s, 'r') as file:
18
+ yaml_content = yaml.safe_load(file)
19
+ if "sap_systems" not in yaml_content:
20
+ raise argparse.ArgumentTypeError("Wrong the YAML file (%s) structure. The 'sap_systems' key not found" % (s,))
21
+ return res
22
+
23
+
24
+ def init_args():
25
+ parser = argparse.ArgumentParser()
26
+ parser.add_argument("-f", "--file", action="store", type=check_file_arg,
27
+ help="File Name (SAP systems (ABAP, JAVA, BO, ...) and their software components in YAML format)", required=True)
28
+ parser.add_argument('--wait', action='store_true', help="Wait 5 minutes and download the report")
29
+ parser.parse_args()
30
+ return vars(parser.parse_args())
31
+
32
+
33
+ def print_errors(errors):
34
+ for error in errors:
35
+ print(error)
36
+
37
+
38
+ def process_it(args):
39
+ systems = process_yaml_file(args)
40
+ additional_keys = dict()
41
+ additional_keys["patch_day"] = True
42
+ offlinesec_client.func.send_to_server(systems, UPLOAD_URL, additional_keys)
43
+
44
+ wait = args["wait"]
45
+ if wait:
46
+ for remaining in range(310, 0, -1):
47
+ sys.stdout.write("\r")
48
+ sys.stdout.write("{:2d} seconds remaining.".format(remaining))
49
+ sys.stdout.flush()
50
+ time.sleep(1)
51
+ os.system("offlinesec_get_reports")
52
+
53
+
54
+ def main():
55
+ args = init_args()
56
+ if FILE in args and args[FILE]:
57
+ process_it(args)
58
+ else:
59
+ print("Please choose the configuration YAML file (-f option)")
60
+
61
+
62
+ if __name__ == '__main__':
63
+ main()
64
+
@@ -0,0 +1,63 @@
1
+ import argparse
2
+ import yaml
3
+ import sys
4
+ import time
5
+ import os
6
+ import offlinesec_client.func
7
+ from offlinesec_client.const import FILE
8
+ from offlinesec_client.multi_systems import process_yaml_file
9
+
10
+
11
+ # SUPPORTED_SYSTEMS = ["ABAP", "JAVA", "BO", "HANA"]
12
+ UPLOAD_URL = "/sec-notes"
13
+
14
+
15
+ def check_file_arg(s):
16
+ res = offlinesec_client.func.check_file_arg(s, ['yaml'], 200000)
17
+ with open(s, 'r') as file:
18
+ yaml_content = yaml.safe_load(file)
19
+ if "sap_systems" not in yaml_content:
20
+ raise argparse.ArgumentTypeError("Wrong the YAML file (%s) structure. The 'sap_systems' key not found" % (s,))
21
+ return res
22
+
23
+
24
+ def init_args():
25
+ parser = argparse.ArgumentParser()
26
+ parser.add_argument("-f", "--file", action="store", type=check_file_arg,
27
+ help="File Name (SAP systems (ABAP, JAVA, BO, ...) and their software components in YAML format)", required=True)
28
+ parser.add_argument('--wait', action='store_true', help="Wait 5 minutes and download the report")
29
+ parser.parse_args()
30
+ return vars(parser.parse_args())
31
+
32
+
33
+ def print_errors(errors):
34
+ for error in errors:
35
+ print(error)
36
+
37
+
38
+ def process_it(args):
39
+ systems = process_yaml_file(args)
40
+ additional_keys = dict()
41
+ offlinesec_client.func.send_to_server(systems, UPLOAD_URL, additional_keys)
42
+
43
+ wait = args["wait"]
44
+ if wait:
45
+ for remaining in range(310, 0, -1):
46
+ sys.stdout.write("\r")
47
+ sys.stdout.write("{:2d} seconds remaining.".format(remaining))
48
+ sys.stdout.flush()
49
+ time.sleep(1)
50
+ os.system("offlinesec_get_reports")
51
+
52
+
53
+ def main():
54
+ args = init_args()
55
+ if FILE in args and args[FILE]:
56
+ process_it(args)
57
+ else:
58
+ print("Please choose the configuration YAML file (-f option)")
59
+
60
+
61
+ if __name__ == '__main__':
62
+ main()
63
+
@@ -8,7 +8,7 @@ from offlinesec_client.const import PARAM_NAME, PARAM_DESC, PARAM_VALUE
8
8
 
9
9
  HIDE_PARAMS = ["FN_BDCALTLOG", "FN_BDCLOG", "FN_EXTRACT", "SAPARGV", "SAPGLOBALHOST", "SAPLOCALHOST",
10
10
  "SAPLOCALHOSTFULL", "SAPPROFILE", "SAPPROFILE_IN_EFFECT", "SAPSYSTEMNAME", "SETENV_.*", "Execute_.*",
11
- "Start_Program_.*", "_\S{2}", "dbs/hdb/schema", "dbs/mss/dbname", "dbs/ora/tnsname", "dbs/syb/dbname",
11
+ "Start_Program_.*", r"_\S{2}", "dbs/hdb/schema", "dbs/mss/dbname", "dbs/ora/tnsname", "dbs/syb/dbname",
12
12
  "enq/server/schema_0", "enque/encni/hostname", "enque/serverhost", "igs/listener/rfc", "igs/mux/ip",
13
13
  "ms/comment", "rdisp/j2ee_profile", "rdisp/j2ee_profile", "rdisp/mshost", "rdisp/msserv", "rdisp/myname",
14
14
  "rlfw/bri/msserv", "rlfw/upg/msserv", "snc/gssapi_lib", "snc/identity/as", "vmcj/debug_proxy/cfg/msHost",
@@ -55,7 +55,8 @@ class RsparamReport:
55
55
  def mask(param_name, param_value):
56
56
  DEFAULT_VALUE = "XXX"
57
57
  for hide_param in HIDE_PARAMS:
58
- res = re.match("^(?i)" + hide_param + "$", param_name)
58
+ re_str = "(?i)^" + hide_param.lower() + "$"
59
+ res = re.match(re_str, param_name)
59
60
  if res:
60
61
  return DEFAULT_VALUE if param_value != "" else ""
61
62
 
@@ -0,0 +1,69 @@
1
+ from datetime import datetime
2
+ import yaml
3
+ import os
4
+
5
+
6
+ class SAPSystem:
7
+ def __init__(self, system_name=""):
8
+ self.system_name = system_name
9
+
10
+ def to_dict(self):
11
+ return self.__dict__
12
+
13
+ @staticmethod
14
+ def parse_exclude_file(exclude_yaml_file, root_dir, system_name):
15
+ if root_dir:
16
+ path = os.path.join(root_dir, exclude_yaml_file)
17
+ else:
18
+ path = exclude_yaml_file
19
+ if not os.path.exists(path):
20
+ raise FileNotFoundError("File %s not found" % (path,))
21
+
22
+ if not exclude_yaml_file.upper().endswith(".YAML"):
23
+ raise ValueError("File {} has wrong extension. Only YAML file supported".format(exclude_yaml_file))
24
+ try:
25
+ with open(path, 'r', encoding="utf-8") as f:
26
+ file_content = yaml.safe_load(f)
27
+ except:
28
+ raise ValueError("File {} has wrong structure. Only YAML files supported".format(path))
29
+ else:
30
+ outlist = list()
31
+ note_num = 0
32
+ for item in file_content:
33
+ note_num += 1
34
+ if "note" not in item.keys():
35
+ print("[WARNING] System {} File {} note {} has no key 'note'".format(system_name,
36
+ exclude_yaml_file,
37
+ str(note_num)))
38
+ continue
39
+
40
+ try:
41
+ note_id = int(item["note"])
42
+
43
+ except ValueError as err:
44
+ print("[WARNING] System {} File {} contains wrong Note ID {}".format(system_name,
45
+ exclude_yaml_file,
46
+ item["note"]))
47
+ continue
48
+
49
+ until_date = item["until"] if "until" in item.keys() else ""
50
+ comment = item["comment"] if "comment" in item.keys() else ""
51
+ if until_date:
52
+ try:
53
+ until = datetime.strptime(until_date, "%m.%d.%Y")
54
+ except ValueError as err:
55
+ print("[WARNING] System {} File {} contains wrong Date {}".format(system_name,
56
+ exclude_yaml_file,
57
+ item["until"]))
58
+ until_date = ""
59
+
60
+ outlist.append((note_id, until_date, comment))
61
+
62
+ return outlist
63
+
64
+
65
+ class SimpleSystem(SAPSystem):
66
+ def __init__(self, system_name="", version=""):
67
+ super().__init__(system_name)
68
+ self.version = version
69
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: offlinesec-client
3
- Version: 1.0.30
3
+ Version: 1.1.0
4
4
  Summary: Offline Security Client
5
5
  Home-page: https://offlinesec.com
6
6
  Author: Offline Security
@@ -88,6 +88,9 @@ offlinesec_get_reports
88
88
  * Our knowledge base is constantly updated and contain all SAP security notes released in 2015-2023. You can find the date of last loaded SAP Security Note in your report.
89
89
  * since version 1.0.29 SAP Business Object systems are supported
90
90
  * since version 1.0.30 SAP JAVA systems are supported
91
+ * since version 1.1.0 Offlinsec tool supports multi-system scan
92
+ * since version 1.1.0 Offlinesec tool supports last patch day scan
93
+ * since version 1.1.2 the API to integrate with SIEM or VM is available in Offlinesec tool
91
94
 
92
95
  2. Profile Parameters/Compliance Analysis (SAP Security Baseline Checks)
93
96
  (Available since version 1.0.12)
@@ -2,20 +2,27 @@ README.md
2
2
  setup.py
3
3
  offlinesec_client/__init__.py
4
4
  offlinesec_client/__main__.py
5
+ offlinesec_client/abap_system.py
5
6
  offlinesec_client/agr_1251.py
7
+ offlinesec_client/bo_system.py
6
8
  offlinesec_client/config.py
7
9
  offlinesec_client/const.py
8
10
  offlinesec_client/cwbntcust.py
9
11
  offlinesec_client/func.py
10
12
  offlinesec_client/get_reports.py
13
+ offlinesec_client/java_system.py
14
+ offlinesec_client/multi_systems.py
11
15
  offlinesec_client/req_bo_notes.py
12
16
  offlinesec_client/req_java_notes.py
13
17
  offlinesec_client/req_notes_report.py
14
18
  offlinesec_client/req_param_report.py
19
+ offlinesec_client/req_patch_day.py
15
20
  offlinesec_client/req_roles_report.py
21
+ offlinesec_client/req_sec_notes.py
16
22
  offlinesec_client/resolve_report.py
17
23
  offlinesec_client/rsparam.py
18
24
  offlinesec_client/sap_gui.py
25
+ offlinesec_client/sap_system.py
19
26
  offlinesec_client.egg-info/PKG-INFO
20
27
  offlinesec_client.egg-info/SOURCES.txt
21
28
  offlinesec_client.egg-info/dependency_links.txt
@@ -3,7 +3,9 @@ offlinesec_bo_notes = offlinesec_client.req_bo_notes:main
3
3
  offlinesec_get_reports = offlinesec_client.get_reports:main
4
4
  offlinesec_inverse_transform = offlinesec_client.resolve_report:main
5
5
  offlinesec_java_notes = offlinesec_client.req_java_notes:main
6
+ offlinesec_patch_day = offlinesec_client.req_patch_day:main
6
7
  offlinesec_sap_notes = offlinesec_client.req_notes_report:main
7
8
  offlinesec_sap_params = offlinesec_client.req_param_report:main
8
9
  offlinesec_sap_roles = offlinesec_client.req_roles_report:main
10
+ offlinesec_sec_notes = offlinesec_client.req_sec_notes:main
9
11
 
@@ -1,5 +1,8 @@
1
1
  requests>=2.5.0
2
2
  openpyxl>=3.0.0
3
+ setuptools~=57.0.0
4
+ PyYAML~=6.0.1
3
5
 
4
6
  [:sys_platform == "win32"]
5
7
  pysapgui>=1.0.7
8
+ PySapGUI~=1.0.7
@@ -18,6 +18,8 @@ setup(
18
18
  long_description_content_type="text/markdown",
19
19
  long_description=long_description,
20
20
  entry_points={'console_scripts': ['offlinesec_sap_notes = offlinesec_client.req_notes_report:main',
21
+ 'offlinesec_sec_notes = offlinesec_client.req_sec_notes:main',
22
+ 'offlinesec_patch_day = offlinesec_client.req_patch_day:main',
21
23
  'offlinesec_get_reports = offlinesec_client.get_reports:main',
22
24
  'offlinesec_sap_params = offlinesec_client.req_param_report:main',
23
25
  'offlinesec_sap_roles = offlinesec_client.req_roles_report:main',
@@ -1 +0,0 @@
1
- __version__ = "1.0.30"