fix: Ensure file operations are restricted to PLC working directory

- Added checks to validate if file paths reside within the PLC working
  directory.
- Logs a warning and prevents operations when paths fall outside the
  permissible directory.

Signed-off-by: Sven Sager <akira@narux.de>
Signed-off-by: Sven Sager <s.sager@kunbus.com>
This commit is contained in:
2026-06-22 11:02:24 +02:00
parent c9877d8230
commit 286b146b5d
+34 -14
View File
@@ -1064,11 +1064,19 @@ class RevPiPyLoad:
:param file_name: File with full path relative to work directory
:return: True on success
"""
file_name = os.path.join(self.plcworkdir, file_name)
if os.path.exists(file_name):
os.remove(file_name)
dirname = os.path.dirname(file_name)
if dirname != self.plcworkdir:
plcworkdir = os.path.realpath(self.plcworkdir)
file_path = os.path.realpath(os.path.join(plcworkdir, file_name))
if os.path.commonpath([plcworkdir, file_path]) != plcworkdir:
proginit.logger.warning(
"file path is not in plc working directory"
)
return False
if os.path.exists(file_path):
os.remove(file_path)
dirname = os.path.dirname(file_path)
if dirname != plcworkdir:
try:
# Try to remove directory, which will work if it is empty
os.rmdir(dirname)
@@ -1104,9 +1112,17 @@ class RevPiPyLoad:
:param file_name: File with full path relative to work directory
:return: Binary object in gzip format
"""
file_name = os.path.join(self.plcworkdir, file_name)
if os.path.exists(file_name):
with open(file_name, "rb") as fh:
plcworkdir = os.path.realpath(self.plcworkdir)
file_path = os.path.realpath(os.path.join(plcworkdir, file_name))
if os.path.commonpath([plcworkdir, file_path]) != plcworkdir:
proginit.logger.warning(
"file path is not in plc working directory"
)
return Binary()
if os.path.exists(file_path):
with open(file_path, "rb") as fh:
xmldata = Binary(gzip.compress(fh.read()))
return xmldata
return Binary()
@@ -1169,7 +1185,7 @@ class RevPiPyLoad:
else:
return -1
def xml_plcupload(self, filedata, filename):
def xml_plcupload(self, filedata: Binary, filename:str):
"""Empfaengt Dateien fuer das PLC Programm einzeln.
@param filedata GZIP Binary data der Datei
@@ -1185,14 +1201,18 @@ class RevPiPyLoad:
# Windowszeichen prüfen
filename = filename.replace("\\", "/")
# Build absolut path, join will return last element, if absolute
dirname = os.path.join(self.plcworkdir, os.path.dirname(filename))
if os.path.abspath(dirname).find(self.plcworkdir) != 0:
plcworkdir = os.path.realpath(self.plcworkdir)
file_path = os.path.realpath(os.path.join(plcworkdir, filename))
if os.path.commonpath([plcworkdir, file_path]) != plcworkdir:
proginit.logger.warning(
"file path is not in plc working directory"
)
return False
dirname = os.path.dirname(file_path)
set_uid = self.plcuid if self.plcworkdir_set_uid else 0
set_gid = self.plcgid if self.plcworkdir_set_uid else 0
@@ -1210,9 +1230,9 @@ class RevPiPyLoad:
# Datei erzeugen
try:
with open(filename, "wb") as fh:
with open(file_path, "wb") as fh:
fh.write(gzip.decompress(filedata.data))
os.chown(filename, set_uid, set_gid)
os.chown(file_path, set_uid, set_gid)
return True
except Exception:
return False