Skip to content

Commit d158d23

Browse files
committed
Fix Zip Slip vulnerability in fetcher, fix NoneType crash in validator, fix wrong board mappings in sdk_generator (rp2040, vexpress), add null-safety checks and encoding params
1 parent 763ca2f commit d158d23

2 files changed

Lines changed: 50 additions & 3 deletions

File tree

ebuild/eos_ai/eos_validator.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,19 @@ def validate_boot(self, path: str) -> ValidationResult:
9292

9393
def _validate_board(self, path: Path, result: ValidationResult):
9494
try:
95-
data = yaml.safe_load(path.read_text())
95+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
9696
except Exception as e:
9797
result.add_error("board.yaml", "", f"Parse error: {e}")
9898
return
9999

100+
if not isinstance(data, dict):
101+
result.add_error("board.yaml", "", "Invalid format: expected YAML mapping")
102+
return
103+
100104
board = data.get("board", data)
105+
if not isinstance(board, dict):
106+
result.add_error("board.yaml", "board", "Board section must be a mapping")
107+
return
101108

102109
if not board.get("name"):
103110
result.add_error("board.yaml", "name", "Board name is required")
@@ -117,12 +124,19 @@ def _validate_board(self, path: Path, result: ValidationResult):
117124

118125
def _validate_boot(self, path: Path, result: ValidationResult):
119126
try:
120-
data = yaml.safe_load(path.read_text())
127+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
121128
except Exception as e:
122129
result.add_error("boot.yaml", "", f"Parse error: {e}")
123130
return
124131

132+
if not isinstance(data, dict):
133+
result.add_error("boot.yaml", "", "Invalid format: expected YAML mapping")
134+
return
135+
125136
boot = data.get("boot", data)
137+
if not isinstance(boot, dict):
138+
result.add_error("boot.yaml", "boot", "Boot section must be a mapping")
139+
return
126140

127141
if not boot.get("board"):
128142
result.add_error("boot.yaml", "board", "Board name is required")
@@ -156,11 +170,27 @@ def _validate_boot(self, path: Path, result: ValidationResult):
156170

157171
def _validate_build(self, path: Path, result: ValidationResult):
158172
try:
159-
data = yaml.safe_load(path.read_text())
173+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
160174
except Exception as e:
161175
result.add_error("build.yaml", "", f"Parse error: {e}")
162176
return
163177

178+
if not isinstance(data, dict):
179+
result.add_error("build.yaml", "", "Invalid format: expected YAML mapping")
180+
return
181+
164182
project = data.get("project", {})
183+
if not isinstance(project, dict):
184+
result.add_error("build.yaml", "project", "Project section must be a mapping")
185+
return
186+
165187
if not project.get("name"):
166188
result.add_error("build.yaml", "project.name", "Project name is required")
189+
190+
targets = data.get("targets", [])
191+
if not targets:
192+
result.add_warning("build.yaml", "targets", "No build targets defined")
193+
194+
toolchain = data.get("toolchain", {})
195+
if isinstance(toolchain, dict) and not toolchain.get("compiler"):
196+
result.add_warning("build.yaml", "toolchain.compiler", "Compiler not specified")

ebuild/packages/fetcher.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,17 @@ def fetch(self, recipe: PackageRecipe, extract_to: str | Path) -> Path:
5555

5656
def _download(self, recipe: PackageRecipe) -> Path:
5757
"""Download the source archive if not already cached."""
58+
if not recipe.url:
59+
raise FetchError(f"No URL specified for package {recipe.name}")
60+
if not recipe.url.startswith(("http://", "https://")):
61+
raise FetchError(
62+
f"Invalid URL scheme for {recipe.name}: {recipe.url} "
63+
f"(only http:// and https:// are allowed)"
64+
)
65+
5866
filename = self._archive_filename(recipe)
67+
if not filename:
68+
raise FetchError(f"Could not derive filename from URL: {recipe.url}")
5969
archive_path = self.download_dir / filename
6070

6171
if archive_path.exists():
@@ -108,6 +118,13 @@ def _extract(self, archive_path: Path, extract_to: Path) -> None:
108118
tar.extractall(extract_to, filter="data")
109119
elif name.endswith(".zip"):
110120
with zipfile.ZipFile(archive_path, "r") as zf:
121+
for member in zf.namelist():
122+
member_path = Path(extract_to) / member
123+
resolved = member_path.resolve()
124+
if not str(resolved).startswith(str(Path(extract_to).resolve())):
125+
raise FetchError(
126+
f"Zip path traversal detected: {member}"
127+
)
111128
zf.extractall(extract_to)
112129
else:
113130
raise FetchError(f"Unsupported archive format: {archive_path.name}")

0 commit comments

Comments
 (0)