Skip to content

Commit fa732ac

Browse files
committed
Fix some potential security issues
Assisted by: Claude code
1 parent 91005ca commit fa732ac

File tree

2 files changed

+63
-39
lines changed

2 files changed

+63
-39
lines changed

charon/pkgs/maven.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
META_FILE_FAILED, MAVEN_METADATA_TEMPLATE,
3333
ARCHETYPE_CATALOG_TEMPLATE, ARCHETYPE_CATALOG_FILENAME,
3434
PACKAGE_TYPE_MAVEN)
35-
from typing import Dict, List, Tuple
35+
from typing import Dict, List, Tuple, Union
3636
from jinja2 import Template
3737
from datetime import datetime
3838
from zipfile import ZipFile, BadZipFile
@@ -217,7 +217,8 @@ def parse_gavs(pom_paths: List[str], root="/") -> Dict[str, Dict[str, List[str]]
217217
return gavs
218218

219219

220-
def gen_meta_file(group_id, artifact_id: str, versions: list, root="/", digest=True) -> List[str]:
220+
def gen_meta_file(group_id, artifact_id: str,
221+
versions: list, root="/", do_digest=True) -> List[str]:
221222
content = MavenMetadata(
222223
group_id, artifact_id, versions
223224
).generate_meta_file_content()
@@ -229,7 +230,7 @@ def gen_meta_file(group_id, artifact_id: str, versions: list, root="/", digest=T
229230
meta_files.append(final_meta_path)
230231
except FileNotFoundError as e:
231232
raise e
232-
if digest:
233+
if do_digest:
233234
meta_files.extend(__gen_all_digest_files(final_meta_path))
234235
return meta_files
235236

@@ -782,7 +783,7 @@ def _merge_directories_with_rename(src_dir: str, dest_dir: str, root: str):
782783
_handle_archetype_catalog_merge(src_file, dest_file)
783784
merged_count += 1
784785
logger.debug("Merged archetype catalog: %s -> %s", src_file, dest_file)
785-
if os.path.exists(dest_file):
786+
elif os.path.exists(dest_file):
786787
duplicated_count += 1
787788
logger.debug("Duplicated: %s, skipped", dest_file)
788789
else:
@@ -1303,8 +1304,8 @@ def __wildcard_metadata_paths(paths: List[str]) -> List[str]:
13031304
new_paths.append(path[:-len(".xml")] + ".*")
13041305
elif path.endswith(".md5")\
13051306
or path.endswith(".sha1")\
1306-
or path.endswith(".sha128")\
1307-
or path.endswith(".sha256"):
1307+
or path.endswith(".sha256")\
1308+
or path.endswith(".sha512"):
13081309
continue
13091310
else:
13101311
new_paths.append(path)
@@ -1313,7 +1314,7 @@ def __wildcard_metadata_paths(paths: List[str]) -> List[str]:
13131314

13141315
class VersionCompareKey:
13151316
'Used as key function for version sorting'
1316-
def __init__(self, obj):
1317+
def __init__(self, obj: str):
13171318
self.obj = obj
13181319

13191320
def __lt__(self, other):
@@ -1344,19 +1345,19 @@ def __compare(self, other) -> int:
13441345
big = max(len(xitems), len(yitems))
13451346
for i in range(big):
13461347
try:
1347-
xitem = xitems[i]
1348+
xitem: Union[str, int] = xitems[i]
13481349
except IndexError:
13491350
return -1
13501351
try:
1351-
yitem = yitems[i]
1352+
yitem: Union[str, int] = yitems[i]
13521353
except IndexError:
13531354
return 1
1354-
if xitem.isnumeric() and yitem.isnumeric():
1355+
if isinstance(xitem, str) and isinstance(yitem, str) and xitem.isnumeric() and yitem.isnumeric():
13551356
xitem = int(xitem)
13561357
yitem = int(yitem)
1357-
elif xitem.isnumeric() and not yitem.isnumeric():
1358+
elif isinstance(xitem, str) and xitem.isnumeric() and (not isinstance(yitem, str) or not yitem.isnumeric()):
13581359
return 1
1359-
elif not xitem.isnumeric() and yitem.isnumeric():
1360+
elif isinstance(yitem, str) and yitem.isnumeric() and (not isinstance(xitem, str) or not xitem.isnumeric()):
13601361
return -1
13611362
if xitem > yitem:
13621363
return 1
@@ -1367,13 +1368,28 @@ def __compare(self, other) -> int:
13671368
return 0
13681369

13691370

1370-
class ArchetypeCompareKey(VersionCompareKey):
1371-
'Used as key function for GAV sorting'
1372-
def __init__(self, gav):
1373-
super().__init__(gav.version)
1371+
class ArchetypeCompareKey:
1372+
def __init__(self, gav: ArchetypeRef):
13741373
self.gav = gav
13751374

1376-
# pylint: disable=unused-private-member
1375+
def __lt__(self, other):
1376+
return self.__compare(other) < 0
1377+
1378+
def __gt__(self, other):
1379+
return self.__compare(other) > 0
1380+
1381+
def __le__(self, other):
1382+
return self.__compare(other) <= 0
1383+
1384+
def __ge__(self, other):
1385+
return self.__compare(other) >= 0
1386+
1387+
def __eq__(self, other):
1388+
return self.__compare(other) == 0
1389+
1390+
def __hash__(self):
1391+
return self.gav.__hash__()
1392+
13771393
def __compare(self, other) -> int:
13781394
x = self.gav.group_id + ":" + self.gav.artifact_id
13791395
y = other.gav.group_id + ":" + other.gav.artifact_id

charon/utils/files.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import os
1818
import hashlib
1919
import errno
20-
from typing import List, Tuple
20+
import tempfile
21+
import shutil
22+
from typing import List, Tuple, Optional
2123
from charon.constants import MANIFEST_SUFFIX
2224

2325

@@ -32,24 +34,37 @@ class HashType(Enum):
3234

3335
def get_hash_type(type_str: str) -> HashType:
3436
"""Get hash type from string"""
35-
if type_str.lower() == "md5":
37+
type_str_low = type_str.lower()
38+
if type_str_low == "md5":
3639
return HashType.MD5
37-
elif type_str.lower() == "sha1":
40+
elif type_str_low == "sha1":
3841
return HashType.SHA1
39-
elif type_str.lower() == "sha256":
42+
elif type_str_low == "sha256":
4043
return HashType.SHA256
41-
elif type_str.lower() == "sha512":
44+
elif type_str_low == "sha512":
4245
return HashType.SHA512
4346
else:
4447
raise ValueError("Unsupported hash type: {}".format(type_str))
4548

4649

47-
def overwrite_file(file_path: str, content: str):
48-
if not os.path.isfile(file_path):
49-
with open(file_path, mode="a", encoding="utf-8"):
50-
pass
51-
with open(file_path, mode="w", encoding="utf-8") as f:
52-
f.write(content)
50+
def overwrite_file(file_path: str, content: str) -> None:
51+
parent_dir: Optional[str] = os.path.dirname(file_path)
52+
if parent_dir:
53+
if not os.path.exists(parent_dir):
54+
os.makedirs(parent_dir, exist_ok=True)
55+
else:
56+
parent_dir = None # None explicitly means current directory for tempfile
57+
58+
# Write to temporary file first, then atomically rename
59+
fd, temp_path = tempfile.mkstemp(dir=parent_dir, text=True)
60+
try:
61+
with os.fdopen(fd, 'w', encoding='utf-8') as f:
62+
f.write(content)
63+
shutil.move(temp_path, file_path)
64+
except Exception:
65+
if os.path.exists(temp_path):
66+
os.unlink(temp_path)
67+
raise
5368

5469

5570
def read_sha1(file: str) -> str:
@@ -97,7 +112,6 @@ def digest_content(content: str, hash_type=HashType.SHA1) -> str:
97112

98113

99114
def _hash_object(hash_type: HashType):
100-
hash_obj = None
101115
if hash_type == HashType.SHA1:
102116
hash_obj = hashlib.sha1()
103117
elif hash_type == HashType.SHA256:
@@ -107,7 +121,7 @@ def _hash_object(hash_type: HashType):
107121
elif hash_type == HashType.SHA512:
108122
hash_obj = hashlib.sha512()
109123
else:
110-
raise Exception("Error: Unknown hash type for digesting.")
124+
raise ValueError("Error: Unknown hash type for digesting.")
111125
return hash_obj
112126

113127

@@ -116,14 +130,8 @@ def write_manifest(paths: List[str], root: str, product_key: str) -> Tuple[str,
116130
manifest_path = os.path.join(root, manifest_name)
117131
artifacts = []
118132
for path in paths:
119-
if path.startswith(root):
120-
path = path[len(root):]
121-
if path.startswith("/"):
122-
path = path[1:]
123-
artifacts.append(path)
124-
125-
if not os.path.isfile(manifest_path):
126-
with open(manifest_path, mode="a", encoding="utf-8"):
127-
pass
133+
rel_path = os.path.relpath(path, root)
134+
artifacts.append(rel_path)
135+
128136
overwrite_file(manifest_path, '\n'.join(artifacts))
129137
return manifest_name, manifest_path

0 commit comments

Comments
 (0)