Skip to content

Commit 05ac077

Browse files
committed
security: add type checking
1 parent f3af44a commit 05ac077

File tree

1 file changed

+36
-33
lines changed

1 file changed

+36
-33
lines changed

hooks/security.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
from collections import Counter
44
from dataclasses import dataclass
55
from pathlib import Path
6+
from re import Match
67
from tempfile import NamedTemporaryFile
8+
from typing import List
79

810
from checksec.elf import ELFSecurity, PIEType, RelroType, set_libc
911
from checksec.errors import ErrorNotAnElf, ErrorParsingFailed
12+
from guestfs import GuestFS
1013
from see import Hook
1114

1215
from hooks.filesystem import Inode
13-
from oswatcher.model import OSType
16+
from oswatcher.model import OS, OSType
1417

1518

1619
@dataclass
@@ -44,20 +47,20 @@ class SecurityHook(Hook):
4447
def __init__(self, parameters):
4548
super().__init__(parameters)
4649
self.os_info = None
47-
self.stats = Counter()
50+
self.stats: Counter = Counter()
4851
self.stats['total'] = 0
4952
self.local_guest_libc = NamedTemporaryFile()
50-
self.neo4j_enabled = self.configuration.get('neo4j', False)
53+
self.neo4j_enabled: bool = self.configuration.get('neo4j', False)
5154
if self.neo4j_enabled:
52-
self.os_node = self.configuration['neo4j']['OS']
53-
self.keep_binaries = self.configuration.get('keep_failed_binaries', False)
55+
self.os_node: OS = self.configuration['neo4j']['OS']
56+
self.keep_binaries: bool = self.configuration.get('keep_failed_binaries', False)
5457
# directory to dump executable on which checksec failed
5558
if self.neo4j_enabled:
5659
os_id = self.os_node.id
5760
else:
5861
os_id = self.context.domain.name()
59-
default_checksec_failed_dir = Path.cwd() / f"{os_id}_checksec_failed"
60-
self.keep_binaries_dir = self.configuration.get('keep_failed_dir', default_checksec_failed_dir)
62+
default_checksec_failed_dir: Path = Path.cwd() / f"{os_id}_checksec_failed"
63+
self.keep_binaries_dir: Path = Path(self.configuration.get('keep_failed_dir', default_checksec_failed_dir))
6164

6265
self.context.subscribe('detected_os_info', self.get_os_info)
6366
self.context.subscribe('filesystem_capture_begin', self.download_libc)
@@ -68,7 +71,7 @@ def get_os_info(self, event):
6871

6972
def download_libc(self, event):
7073
"""Locate and download the libc"""
71-
gfs = event.gfs
74+
gfs: GuestFS = event.gfs
7275

7376
if not self.os_info:
7477
raise RuntimeError('Expected OS Info')
@@ -77,35 +80,35 @@ def download_libc(self, event):
7780
return
7881

7982
# find ldd
80-
cmd = ['which', 'ldd']
83+
cmd: List = ['which', 'ldd']
8184
try:
82-
ldd_path = gfs.command(cmd).strip()
85+
ldd_path: str = gfs.command(cmd).strip()
8386
except RuntimeError:
8487
self.logger.warning("Libc detection: command %s failed", cmd)
8588
return
8689
# find ls
87-
cmd = ['which', 'ls']
90+
cmd: List = ['which', 'ls']
8891
try:
89-
ls_path = gfs.command(cmd).strip()
92+
ls_path: str = gfs.command(cmd).strip()
9093
except RuntimeError:
9194
self.logger.warning("Libc detection: command %s failed", cmd)
9295
return
93-
cmd = [ldd_path, ls_path]
96+
cmd: List = [ldd_path, ls_path]
9497
try:
95-
ldd_output = gfs.command(cmd).strip()
98+
ldd_output: str = gfs.command(cmd).strip()
9699
except RuntimeError:
97100
self.logger.warning("Libc detection: command %s failed", cmd)
98101
return
99102

100103
libc_inode = None
101104
for ldd_line in ldd_output.splitlines():
102-
m = re.match(r'\t*(?P<libname>.*)\s+(=>)?\s+(?P<libpath>\S+)?\s+\((?P<addr>.*)\)$', ldd_line)
105+
m: Match = re.match(r'\t*(?P<libname>.*)\s+(=>)?\s+(?P<libpath>\S+)?\s+\((?P<addr>.*)\)$', ldd_line)
103106
if not m:
104107
self.logger.warn("Libc detection: line \"%s\" doesn't match LDD regex", ldd_line)
105108
continue
106109
if m.group('libname').startswith('libc.so'):
107110
# found guest libc
108-
libc_inode = Inode(self.logger, gfs, Path(m.group('libpath')))
111+
libc_inode: Inode = Inode(self.logger, gfs, Path(m.group('libpath')))
109112
break
110113
if libc_inode is None:
111114
self.logger.warning("Libc detection: Couldn't locate libc !")
@@ -118,21 +121,21 @@ def download_libc(self, event):
118121

119122
def check_file(self, event):
120123
# event args
121-
inode = event.inode
124+
inode: Inode = event.inode
122125

123126
if not self.os_info['os_type'] == OSType.Linux:
124127
# checksec only supports ELF files
125128
return
126-
mime = inode.file_magic_type
127-
filepath = inode.path
129+
mime: str = inode.file_magic_type
130+
filepath: Path = inode.path
128131
if re.match(r'application/x(-pie)?-(executable|sharedlib)', mime):
129132
self.logger.info('Checking security of %s: %s', filepath, mime)
130133
self.stats['total'] += 1
131134
# this is a heavy call (download the file on the host filesystem through libguestfs appliance)
132135
# call it here once we filtered on the mime type provided by the file utility
133-
local_filepath = inode.local_file
136+
local_filepath: Path = inode.local_file
134137
try:
135-
elf = ELFSecurity(local_filepath)
138+
elf: ELFSecurity = ELFSecurity(local_filepath)
136139
except ErrorNotAnElf:
137140
self.stats['failed'] += 1
138141
self.logger.warning("Not a valid ELF file: %s (%s)", filepath, inode.gfs_file)
@@ -148,18 +151,18 @@ def check_file(self, event):
148151
shutil.copy(inode.local_file, dst)
149152
return
150153
else:
151-
relro = elf.relro
152-
canary = elf.has_canary
153-
nx = elf.has_nx
154-
pie = elf.pie
155-
rpath = elf.has_rpath
156-
runpath = elf.has_runpath
157-
symbols = not elf.is_stripped
158-
fortified = elf.is_fortified
159-
fortify_source = len(elf.fortified)
160-
fortifyable = len(elf.fortifiable)
161-
162-
checksec_file = ChecksecFile(relro, canary, nx, pie, rpath, runpath,
154+
relro: RelroType = elf.relro
155+
canary: bool = elf.has_canary
156+
nx: bool = elf.has_nx
157+
pie: PIEType = elf.pie
158+
rpath: bool = elf.has_rpath
159+
runpath: bool = elf.has_runpath
160+
symbols: bool = not elf.is_stripped
161+
fortified: bool = elf.is_fortified
162+
fortify_source: int = len(elf.fortified)
163+
fortifyable: int = len(elf.fortifiable)
164+
165+
checksec_file: ChecksecFile = ChecksecFile(relro, canary, nx, pie, rpath, runpath,
163166
symbols, fortify_source, fortified, fortifyable)
164167
self.logger.debug("Properties: %s", checksec_file)
165168
self.context.trigger('security_checksec_bin', inode=inode, checksec_file=checksec_file)

0 commit comments

Comments
 (0)