Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ assets/demo-data/CLEWs.Demo.zip

Procfile
.env
.runtime/
WebAPP/app.log
dist/
build/
venv/
Expand Down
28 changes: 27 additions & 1 deletion API/Classes/Base/Config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from pathlib import Path
import os
import tempfile
from dotenv import load_dotenv
import platform

# Central path validation utility (prevents path traversal)
# Central path validation utility (prevents path traversal).
def validate_path(base_dir, user_input):
base_raw = os.fspath(base_dir)
user_raw = "" if user_input is None else os.fspath(user_input)
Expand Down Expand Up @@ -62,13 +63,38 @@ def validate_path(base_dir, user_input):
CLASS_FOLDER = WEBAPP_PATH / "Classes"
SOLVERs_FOLDER = WEBAPP_PATH / "SOLVERs"
EXTRACT_FOLDER = BASE_DIR
PRIMARY_RUNTIME_DIR = BASE_DIR / ".runtime"
PRIMARY_RUNTIME_LOG_DIR = PRIMARY_RUNTIME_DIR / "logs"
TEMP_RUNTIME_LOG_DIR = Path(tempfile.gettempdir()) / "muiogo-runtime" / "logs"
_RUNTIME_LOG_PATH = None

# Ensure DataStorage exists
DATA_STORAGE.mkdir(parents=True, exist_ok=True)

# Validate writability instead of forcing permissions
if not os.access(DATA_STORAGE, os.W_OK):
raise PermissionError(f"Data storage path is not writable: {DATA_STORAGE}")


def get_runtime_log_path():
global _RUNTIME_LOG_PATH

if _RUNTIME_LOG_PATH is not None:
return _RUNTIME_LOG_PATH

for log_dir in (PRIMARY_RUNTIME_LOG_DIR, TEMP_RUNTIME_LOG_DIR):
try:
log_dir.mkdir(parents=True, exist_ok=True)
probe = log_dir / ".write_test"
with open(probe, "a", encoding="utf-8"):
pass
probe.unlink(missing_ok=True)
_RUNTIME_LOG_PATH = log_dir / "app.log"
return _RUNTIME_LOG_PATH
except OSError:
continue

raise PermissionError("No writable runtime log directory is available.")
#absolute paths
# OSEMOSYS_ROOT = os.path.abspath(os.getcwd())
# UPLOAD_FOLDER = Path(OSEMOSYS_ROOT, 'WebAPP')
Expand Down
119 changes: 51 additions & 68 deletions API/Classes/Case/DataFileClass.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@
# self.f = open(self.dataFile, mode="w", encoding='utf-8')
#self.f = open(dataFilePath, mode="w", encoding='utf-8')

with open(dataFilePath, "w", encoding="utf-8") as self.f:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
#f.write(json.dumps(data, ensure_ascii=False, indent=4, sort_keys=False))
self.f.write('####################\n#Sets#\n####################\n')
self.f.write('{} {}'.format('#', '\n'))
Expand Down Expand Up @@ -792,17 +792,16 @@
try:
caseRunPath = Path(Config.DATA_STORAGE,self.case,'res', caserunname)
csvPath = Path(Config.DATA_STORAGE,self.case,'res', caserunname, 'csv')
resDataPath = Path(Config.DATA_STORAGE,self.case,'view', 'resData.json')

if not os.path.exists(caseRunPath):
os.makedirs(caseRunPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
os.makedirs(csvPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
if not os.path.exists(resDataPath):
File.writeFile( data, resDataPath)
if not os.path.exists(self.resDataPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
self.resData = {"osy-cases": [data]}
File.writeFile(self.resData, self.resDataPath)
else:
resData = File.readFile(resDataPath)
resData['osy-cases'].append(data)
File.writeFile( resData, resDataPath)
self.resData['osy-cases'].append(data)
File.writeFile(self.resData, self.resDataPath)
response = {
"message": "You have created a case run!",
"status_code": "success"
Expand All @@ -822,16 +821,15 @@

def deleteScenarioCaseRuns(self, scenarioId):
try:
resData = File.readFile(self.resDataPath)
cases = resData['osy-cases']
cases = self.resData['osy-cases']

for cs in cases:
for sc in cs['Scenarios']:
if sc['ScenarioId'] == scenarioId:
cs['Scenarios'].remove(sc)


File.writeFile(resData, self.resDataPath)
File.writeFile(self.resData, self.resDataPath)
response = {
"message": "You have deleted scenario from caseruns!",
"status_code": "success"
Expand All @@ -850,38 +848,33 @@
caseRunPath = Path(Config.DATA_STORAGE,self.case,'res', oldcaserunname)
newcaseRunPath = Path(Config.DATA_STORAGE,self.case,'res', caserunname)
csvPath = Path(Config.DATA_STORAGE,self.case,'res', caserunname, 'csv')
resDataPath = Path(Config.DATA_STORAGE,self.case,'view', 'resData.json')

if not os.path.exists(newcaseRunPath):
os.rename(caseRunPath, newcaseRunPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

if not os.path.exists(csvPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
os.makedirs(csvPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

resData = File.readFile(resDataPath)

resdata = resData['osy-cases']
resdata = self.resData['osy-cases']
for i, case in enumerate(resdata):
if case['Case'] == oldcaserunname:
resData['osy-cases'][i] = data
self.resData['osy-cases'][i] = data

File.writeFile( resData, resDataPath)
File.writeFile(self.resData, self.resDataPath)
response = {
"message": "You have updated a case run!",
"status_code": "success"
}
elif os.path.exists(newcaseRunPath) and caserunname==oldcaserunname:
if not os.path.exists(csvPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
os.makedirs(csvPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

resData = File.readFile(resDataPath)

resdata = resData['osy-cases']
resdata = self.resData['osy-cases']
for i, case in enumerate(resdata):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
if case['Case'] == oldcaserunname:
resData['osy-cases'][i] = data
self.resData['osy-cases'][i] = data

File.writeFile( resData, resDataPath)
File.writeFile(self.resData, self.resDataPath)
response = {
"message": "You have updated a case run!",
"status_code": "success"
Expand Down Expand Up @@ -909,13 +902,13 @@
#if group != 'RYS':
path = Path(self.viewFolderPath, group+'.json')
if path.is_file():
jsonFile = File.readFile(path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
for obj in array:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
#potrebna provjera jer smo u 4.5 verziji dodali varijablu EBAC i dolazilo je do greske jer nije bilo u reyultataima
if obj['id'] in jsonFile:
if caserunname in jsonFile[obj['id']]:
del jsonFile[obj['id']][caserunname]
File.writeFile(jsonFile, path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
except(IOError, IndexError):
raise IndexError
except OSError:
Expand All @@ -924,17 +917,15 @@
def deleteCaseRun(self, caserunname, resultsOnly):
try:
#caseRunPath = Path(Config.DATA_STORAGE,self.case,'res', caserunname)
#resDataPath = Path(Config.DATA_STORAGE,self.case,'view', 'resData.json')
#self.resData = Path(Config.DATA_STORAGE,self.case,'view', 'resData.json')


if not resultsOnly:
resData = File.readFile(self.resDataPath)

for obj in resData['osy-cases']:
for obj in self.resData['osy-cases']:
if obj['Case'] == caserunname:
resData['osy-cases'].remove(obj)
self.resData['osy-cases'].remove(obj)

File.writeFile( resData, self.resDataPath)
File.writeFile(self.resData, self.resDataPath)

#delete from view folder
for group, array in self.VARIABLES.items():
Expand All @@ -949,7 +940,7 @@
File.writeFile(jsonFile, path)

response = {
"message": "You have deleted a case run!",

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
"status_code": "success"
}

Expand All @@ -970,45 +961,38 @@
# self.viewFolderPath = Path(Config.DATA_STORAGE,case,'view')
# folder_path = "C:/putanja/do/foldera"

for caserunname in os.listdir( self.resultsPath):
caserunname_path = os.path.join(self.resultsPath, caserunname)
# Skip files such as .DS_Store that can appear on macOS.
if not os.path.isdir(caserunname_path):
continue
for carerunData in os.listdir( caserunname_path):
file_path = os.path.join(caserunname_path, carerunData)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.remove(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print(f"Greška pri brisanju {file_path}: {e}")

for filename in os.listdir( self.viewFolderPath):
if filename !='resData.json':
file_path = os.path.join(self.viewFolderPath, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.remove(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print(f"Greška pri brisanju {file_path}: {e}")

#sad moramo napraviti defualt definitions file
viewDefPath = Path(self.viewFolderPath, 'viewDefinitions.json')
configPath = Path(Config.DATA_STORAGE, 'Variables.json')
vars = File.readParamFile(configPath)
viewDef = {}
for group, lists in vars.items():
for list in lists:
viewDef[list['id']] = []

viewData = {
"osy-views": viewDef
}
File.writeFile( viewData, viewDefPath)
if os.path.exists(self.resultsPath) and os.path.isdir(self.resultsPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
for caserunname in os.listdir(self.resultsPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
caserunname_path = os.path.join(self.resultsPath, caserunname)
if not os.path.isdir(caserunname_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
continue
for carerunData in os.listdir(caserunname_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
file_path = os.path.join(caserunname_path, carerunData)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
os.remove(file_path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
elif os.path.isdir(file_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
shutil.rmtree(file_path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
except Exception as e:
print(f"Greška pri brisanju {file_path}: {e}")

if os.path.exists(self.viewFolderPath) and os.path.isdir(self.viewFolderPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
for filename in os.listdir(self.viewFolderPath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
if filename not in {'resData.json', 'viewDefinitions.json'}:
file_path = os.path.join(self.viewFolderPath, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
os.remove(file_path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
elif os.path.isdir(file_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
shutil.rmtree(file_path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
except Exception as e:
print(f"Greška pri brisanju {file_path}: {e}")

case_names = [c["Case"] for c in self.resData.get("osy-cases", [])]
for case in case_names:
case_path = Path(self.resultsPath, case)
if not case_path.exists():

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
case_path.mkdir(parents=True, exist_ok=True)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed


response = {
Expand Down Expand Up @@ -1072,10 +1056,9 @@

#f = open(self.dataFile, mode="r")
dataFilePath = Path(Config.DATA_STORAGE, self.case, 'res',caserunname,'data.txt')
if os.path.exists(dataFilePath):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
f = open(dataFilePath, mode="r", encoding='utf-8-sig')
data = f.read()
f.close
with open(dataFilePath, mode="r", encoding='utf-8-sig') as f:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
Comment thread
SeaCelo marked this conversation as resolved.
Dismissed
data = f.read()
else:
data = None

Expand Down Expand Up @@ -1110,7 +1093,7 @@
years = line.rstrip(':= ;\n').split(' ')[0:]
years = [i.strip(':=') for i in years]

else:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
values = line.rstrip().split(' ')[1:]
if param_current in ('DiscountRate'):
region = line.split(' ')[0]
Expand Down Expand Up @@ -1209,7 +1192,7 @@
# tech = element[1]
# fuel_emi = element[2]

# elif line.startswith(start_year):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
# years = line.rstrip(':= ;\n').split(' ')[0:]
# years = [i.strip(':=') for i in years]

Expand Down Expand Up @@ -1663,7 +1646,7 @@
input_fuel_list = []
data = {}
with open(data_infile, 'r') as f:
parsing = False

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
for line in f:
line = line.rstrip().replace('\t', ' ')
if line.startswith(";"):
Expand All @@ -1679,7 +1662,7 @@
years = line.rstrip(':= ;\n').split(' ')[0:]
years = [i.strip(':=') for i in years]

else:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
values = line.rstrip().split(' ')[1:]
if param_current in ('DiscountRate'):
region = line.split(' ')[0]
Expand Down Expand Up @@ -1943,7 +1926,7 @@

#function for appending values in data file
def file_output_function(dict, set_list, set_name, extra_char, type=None):
for each in set_list:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

if each in dict.keys():
line = set_name + str(each) + ']:=' + str(dict[each]) + extra_char
Expand Down Expand Up @@ -1975,7 +1958,7 @@


line = 'set INPUTxFUEL:=' + ', '.join(input_fuel_list)
file_out.write(line + ';' + '\n')

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

file_out.write('end;')

Expand Down Expand Up @@ -2122,12 +2105,12 @@
raise RuntimeError(
f"Could not find 'glpsol' in resolved folder '{glpfolder}'. "
"Check SOLVER_GLPK_PATH or solver installation."
)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
# respath = self.resPath.resolve()

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
# resCBCPath = self.resCBCPath.resolve()

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
self.deleteCaseResultsJSON(caserunname)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
if solver == 'glpk':
glpk_out = subprocess.run(
[str(glpsol_exec), "-m", modelfile, "-d", datafile, "-o", resultfile],
Expand Down Expand Up @@ -2300,7 +2283,7 @@

ov = {
"r": ['RE1'],
"ObjectiveValue": [optimal_value]

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
}

#load data into a DataFrame object:
Expand Down Expand Up @@ -2562,7 +2545,7 @@
if csv_path.is_file():
df = pd.read_csv(csv_path)
data = df.to_json(orient='records', indent=2)
jsondata = json.loads(data)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

if len(jsondata) != 0:
for param, paramobj in paramByName.items():
Expand All @@ -2576,7 +2559,7 @@
viewData = {}

if paramobj['id'] not in viewData:
viewData[paramobj['id']] = {}

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

# if caserunname not in viewData[paramobj['id']]:
# viewData[paramobj['id']][caserunname] = []
Expand All @@ -2587,7 +2570,7 @@
if paramobj['group'] == 'R':
tmp = {}
for obj in jsondata:
tmp['ObjectiveValue'] = obj[param]

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
viewData[paramobj['id']][caserunname].append(tmp)
path = Path(self.viewFolderPath, paramobj['group']+'.json')
File.writeFile( viewData, path)
Expand Down
1 change: 1 addition & 0 deletions API/Classes/Case/OsemosysClass.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class Osemosys():
def __init__(self, case):
Config.validate_path(Config.DATA_STORAGE, case)
self.case = case
self.PARAMETERS = File.readParamFile(Path(Config.DATA_STORAGE, 'Parameters.json'))
self.VARIABLES = File.readParamFile(Path(Config.DATA_STORAGE, 'Variables.json'))
Expand Down
Loading
Loading