Skip to content

Commit c2486b0

Browse files
committed
test(integration): add integration tests for permission changes
1 parent 1746a5f commit c2486b0

File tree

3 files changed

+280
-4
lines changed

3 files changed

+280
-4
lines changed

tests/event.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class EventType(Enum):
3030
OPEN = 1
3131
CREATION = 2
3232
UNLINK = 3
33+
PERMISSION = 4
3334

3435

3536
class Process:
@@ -169,11 +170,13 @@ def __init__(self,
169170
process: Process,
170171
event_type: EventType,
171172
file: str,
172-
host_path: str = ''):
173+
host_path: str = '',
174+
mode: int | None = None):
173175
self._type: EventType = event_type
174176
self._process: Process = process
175177
self._file: str = file
176178
self._host_path: str = host_path
179+
self._mode: int | None = mode
177180

178181
@property
179182
def event_type(self) -> EventType:
@@ -191,6 +194,10 @@ def file(self) -> str:
191194
def host_path(self) -> str:
192195
return self._host_path
193196

197+
@property
198+
def mode(self) -> int | None:
199+
return self._mode
200+
194201
@override
195202
def __eq__(self, other: Any) -> bool:
196203
if isinstance(other, FileActivity):
@@ -206,11 +213,22 @@ def __eq__(self, other: Any) -> bool:
206213
elif self.event_type == EventType.UNLINK:
207214
return self.file == other.unlink.activity.path and \
208215
self.host_path == other.unlink.activity.host_path
216+
elif self.event_type == EventType.PERMISSION:
217+
return self.file == other.permission.activity.path and \
218+
self.host_path == other.permission.activity.host_path and \
219+
self.mode == other.permission.mode
209220
return False
210221
raise NotImplementedError
211222

212223
@override
213224
def __str__(self) -> str:
214-
return (f'Event(event_type={self.event_type.name}, '
215-
f'process={self.process}, file="{self.file}", '
216-
f'host_path="{self.host_path}")')
225+
s = (f'Event(event_type={self.event_type.name}, '
226+
f'process={self.process}, file="{self.file}", '
227+
f'host_path="{self.host_path}"')
228+
229+
if self.event_type == EventType.PERMISSION:
230+
s += f', mode={self.mode}'
231+
232+
s += ')'
233+
234+
return s

tests/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def _wait_events(self, events: list[Event], ignored: list[Event]):
8888
sleep(0.5)
8989
continue
9090

91+
print(f'Got event: {msg}')
9192
if msg in ignored:
9293
raise ValueError(f'Caught ignored event: {msg}')
9394

tests/test_path_chmod.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import multiprocessing as mp
2+
import os
3+
4+
from event import Event, EventType, Process
5+
6+
7+
def test_chmod(fact, monitored_dir, server):
8+
"""
9+
Tests changing permissions on a file and verifies the corresponding
10+
event is captured by the server
11+
12+
Args:
13+
fact: Fixture for file activity (only required to be runing).
14+
monitored_dir: Temporary directory path for creating the test file.
15+
server: The server instance to communicate with.
16+
"""
17+
# File Under Test
18+
fut = os.path.join(monitored_dir, 'test.txt')
19+
mode = 0o666
20+
os.chmod(fut, mode)
21+
22+
e = Event(process=Process.from_proc(), event_type=EventType.PERMISSION,
23+
file=fut, host_path=fut, mode=mode)
24+
25+
print(f'Waiting for event: {e}')
26+
27+
server.wait_events([e])
28+
29+
30+
def test_multiple(fact, monitored_dir, server):
31+
"""
32+
Tests modifying permissions on multiple files.
33+
34+
Args:
35+
fact: Fixture for file activity (only required to be runing).
36+
monitored_dir: Temporary directory path for creating the test file.
37+
server: The server instance to communicate with.
38+
"""
39+
events = []
40+
process = Process.from_proc()
41+
mode = 0o646
42+
43+
for i in range(3):
44+
fut = os.path.join(monitored_dir, f'{i}.txt')
45+
with open(fut, 'w') as f:
46+
f.write('This is a test')
47+
os.chmod(fut, mode)
48+
49+
events.extend([
50+
Event(process=process, event_type=EventType.CREATION,
51+
file=fut, host_path=''),
52+
Event(process=process, event_type=EventType.PERMISSION,
53+
file=fut, host_path='', mode=mode),
54+
])
55+
56+
server.wait_events(events)
57+
58+
59+
def test_ignored(fact, test_file, ignored_dir, server):
60+
"""
61+
Tests that permission events on ignored files are not captured.
62+
63+
Args:
64+
fact: Fixture for file activity (only required to be running).
65+
monitored_dir: Temporary directory path for creating the test file.
66+
ignored_dir: Temporary directory path that is not monitored by fact.
67+
server: The server instance to communicate with.
68+
"""
69+
process = Process.from_proc()
70+
mode = 0o666
71+
72+
# Ignored file, must not show up in the server
73+
ignored_file = os.path.join(ignored_dir, 'test.txt')
74+
with open(ignored_file, 'w') as f:
75+
f.write('This is to be ignored')
76+
os.chmod(ignored_file, mode)
77+
78+
ignored_event = Event(process=process, event_type=EventType.PERMISSION,
79+
file=ignored_file, host_path='', mode=mode)
80+
print(f'Ignoring: {ignored_event}')
81+
82+
# File Under Test
83+
os.chmod(test_file, mode)
84+
85+
e = Event(process=process, event_type=EventType.PERMISSION,
86+
file=test_file, host_path=test_file, mode=mode)
87+
print(f'Waiting foor event: {e}')
88+
89+
server.wait_events([e], ignored=[ignored_event])
90+
91+
92+
def do_test(fut: str, mode: int, stop_event: mp.Event):
93+
with open(fut, 'w') as f:
94+
f.write('This is a test')
95+
os.chmod(fut, mode)
96+
97+
# Wait for test to be done
98+
stop_event.wait()
99+
100+
101+
def test_external_process(fact, monitored_dir, server):
102+
"""
103+
Tests permission change of a file by an external process and
104+
verifies that the corresponding event is captured by the server.
105+
106+
Args:
107+
fact: Fixture for file activity (only required to be running).
108+
monitored_dir: Temporary directory path for creating the test file.
109+
server: The server instance to communicate with.
110+
"""
111+
# File Under Test
112+
fut = os.path.join(monitored_dir, 'test2.txt')
113+
mode = 0o666
114+
stop_event = mp.Event()
115+
proc = mp.Process(target=do_test, args=(fut, mode, stop_event))
116+
proc.start()
117+
process = Process.from_proc(proc.pid)
118+
119+
event = Event(process=process, event_type=EventType.PERMISSION,
120+
file=fut, host_path='', mode=mode)
121+
print(f'Waiting for event: {event}')
122+
123+
try:
124+
server.wait_events([event])
125+
finally:
126+
stop_event.set()
127+
proc.join(1)
128+
129+
130+
def test_overlay(fact, test_container, server):
131+
"""
132+
Test permission changes on an overlayfs file (inside a container)
133+
134+
Args:
135+
fact: Fixture for file activity (only required to be running).
136+
test_container: A container for running commands in.
137+
server: The server instance to communicate with.
138+
"""
139+
# File Under Test
140+
fut = '/container-dir/test.txt'
141+
mode = '666'
142+
143+
# Create the exec and an equivalent event that it will trigger
144+
test_container.exec_run(f'touch {fut}')
145+
test_container.exec_run(f'chmod {mode} {fut}')
146+
147+
loginuid = pow(2, 32)-1
148+
touch = Process(pid=None,
149+
uid=0,
150+
gid=0,
151+
exe_path='/usr/bin/touch',
152+
args=f'touch {fut}',
153+
name='touch',
154+
container_id=test_container.id[:12],
155+
loginuid=loginuid)
156+
chmod = Process(pid=None,
157+
uid=0,
158+
gid=0,
159+
exe_path='/usr/bin/chmod',
160+
args=f'chmod {mode} {fut}',
161+
name='chmod',
162+
container_id=test_container.id[:12],
163+
loginuid=loginuid)
164+
events = [
165+
Event(process=touch, event_type=EventType.CREATION,
166+
file=fut, host_path=''),
167+
Event(process=touch, event_type=EventType.OPEN,
168+
file=fut, host_path=''),
169+
Event(process=chmod, event_type=EventType.PERMISSION,
170+
file=fut, host_path='', mode=int(mode, 8)),
171+
]
172+
173+
for e in events:
174+
print(f'Waiting for event: {e}')
175+
176+
server.wait_events(events)
177+
178+
179+
def test_mounted_dir(fact, test_container, ignored_dir, server):
180+
"""
181+
Test permission changes on a file bind mounted into a container
182+
183+
Args:
184+
fact: Fixture for file activity (only required to be running).
185+
test_container: A container for running commands in.
186+
ignored_dir: This directory is ignored on the host, and mounted to the container.
187+
server: The server instance to communicate with.
188+
"""
189+
# File Under Test
190+
fut = '/mounted/test.txt'
191+
mode = '666'
192+
193+
# Create the exec and an equivalent event that it will trigger
194+
test_container.exec_run(f'touch {fut}')
195+
test_container.exec_run(f'chmod {mode} {fut}')
196+
197+
loginuid = pow(2, 32)-1
198+
touch = Process(pid=None,
199+
uid=0,
200+
gid=0,
201+
exe_path='/usr/bin/touch',
202+
args=f'touch {fut}',
203+
name='touch',
204+
container_id=test_container.id[:12],
205+
loginuid=loginuid)
206+
chmod = Process(pid=None,
207+
uid=0,
208+
gid=0,
209+
exe_path='/usr/bin/chmod',
210+
args=f'chmod {mode} {fut}',
211+
name='chmod',
212+
container_id=test_container.id[:12],
213+
loginuid=loginuid)
214+
events = [
215+
Event(process=touch, event_type=EventType.CREATION, file=fut,
216+
host_path=''),
217+
Event(process=chmod, event_type=EventType.PERMISSION, file=fut,
218+
host_path='', mode=int(mode, 8)),
219+
]
220+
221+
for e in events:
222+
print(f'Waiting for event: {e}')
223+
224+
server.wait_events(events)
225+
226+
227+
def test_unmonitored_mounted_dir(fact, test_container, test_file, server):
228+
"""
229+
Test permission changes on a file bind mounted to a container and
230+
monitored on the host.
231+
232+
Args:
233+
fact: Fixture for file activity (only required to be running).
234+
test_container: A container for running commands in.
235+
test_file: File monitored on the host, mounted to the container.
236+
server: The server instance to communicate with.
237+
"""
238+
# File Under Test
239+
fut = '/unmonitored/test.txt'
240+
mode = '666'
241+
242+
# Create the exec and an equivalent event that it will trigger
243+
test_container.exec_run(f'chmod {mode} {fut}')
244+
245+
process = Process(pid=None,
246+
uid=0,
247+
gid=0,
248+
exe_path='/usr/bin/chmod',
249+
args=f'chmod {mode} {fut}',
250+
name='chmod',
251+
container_id=test_container.id[:12],
252+
loginuid=pow(2, 32)-1)
253+
event = Event(process=process, event_type=EventType.PERMISSION,
254+
file=fut, host_path=test_file, mode=int(mode, 8))
255+
print(f'Waiting for event: {event}')
256+
257+
server.wait_events([event])

0 commit comments

Comments
 (0)