Skip to content

Commit f6a9c0a

Browse files
author
PyCompiler ARK++ Team
committed
feat(header_injector): détection de licence robuste; pas d'injection si absente; i18n via API_SDK (sctx.tr)
1 parent 8e6ff98 commit f6a9c0a

2 files changed

Lines changed: 60 additions & 9 deletions

File tree

API_SDK/__init__.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,50 @@ def wrap_post_context(post_ctx: PostCompileContext, *, log_fn: Optional[Any] = N
188188
"""Wrap an ACASL PostCompileContext into SDKContext for reuse of the same helpers.
189189
- Copies workspace root and artifacts
190190
- Loads workspace config
191+
- Enforces ACASL scope: restrict file operations to engine output directory when provided
191192
"""
192193
try:
193194
ws = Path(getattr(post_ctx, "workspace_root", "."))
194195
except Exception:
195196
ws = Path(".")
196197
root = ws.resolve()
197198
cfg = load_workspace_config(root)
199+
# Resolve allowed output directory (ACASL scope)
200+
allowed_dir = None
198201
try:
199-
arts = list(getattr(post_ctx, "artifacts", []) or [])
202+
od = getattr(post_ctx, "output_dir", None)
203+
if od:
204+
try:
205+
allowed_dir = Path(str(od)).resolve()
206+
except Exception:
207+
allowed_dir = None
208+
except Exception:
209+
allowed_dir = None
210+
# Artifacts: best-effort ensure they are within allowed_dir when set
211+
try:
212+
arts_in = list(getattr(post_ctx, "artifacts", []) or [])
200213
except Exception:
201-
arts = []
202-
return SDKContext(workspace_root=root, config_view=ConfigView(cfg), log_fn=log_fn, engine_id=None, artifacts=arts)
214+
arts_in = []
215+
arts: list[str] = []
216+
if allowed_dir is not None:
217+
for a in arts_in:
218+
try:
219+
rp = Path(a).resolve()
220+
_ = rp.relative_to(allowed_dir)
221+
arts.append(str(rp))
222+
except Exception:
223+
# skip artifacts outside of allowed scope
224+
continue
225+
else:
226+
arts = list(arts_in)
227+
return SDKContext(
228+
workspace_root=root,
229+
config_view=ConfigView(cfg),
230+
log_fn=log_fn,
231+
engine_id=None,
232+
artifacts=arts,
233+
allowed_dir=allowed_dir,
234+
)
203235

204236

205237
# -----------------------------

API_SDK/context.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class SDKContext:
4242
engine_id: Optional[str] = None
4343
plugin_id: Optional[str] = None
4444
artifacts: list[str] = field(default_factory=list)
45+
# ACASL-only scope: when set, all file operations are restricted to this directory
46+
allowed_dir: Optional[Path] = None
4547
redact_logs: bool = True
4648
debug_enabled: bool = False
4749
_iter_cache: dict[tuple[tuple[str, ...], tuple[str, ...]], list[Path]] = field(
@@ -181,10 +183,27 @@ def is_within_workspace(self, p: Path) -> bool:
181183
except Exception:
182184
return False
183185

186+
def is_within_allowed(self, p: Path) -> bool:
187+
"""Return True if path is within the allowed_dir (ACASL scope) when set; otherwise True."""
188+
try:
189+
if self.allowed_dir is None:
190+
return True
191+
_ = p.resolve().relative_to(self.allowed_dir.resolve())
192+
return True
193+
except Exception:
194+
return False
195+
196+
def is_within_scope(self, p: Path) -> bool:
197+
"""Scope check used by file helpers: within workspace and, when set, within allowed_dir."""
198+
try:
199+
return self.is_within_workspace(p) and self.is_within_allowed(p)
200+
except Exception:
201+
return False
202+
184203
def safe_path(self, *parts: Pathish) -> Path:
185204
p = self.path(*parts).resolve()
186-
if not self.is_within_workspace(p):
187-
raise ValueError(f"Path escapes workspace: {p}")
205+
if not self.is_within_scope(p):
206+
raise ValueError(f"Path not allowed (outside output_dir/workspace): {p}")
188207
return p
189208

190209
def require_files(self, paths: Sequence[Pathish]) -> list[Path]:
@@ -220,14 +239,14 @@ def iter_files(
220239
enforce_workspace: bool = True,
221240
max_files: Optional[int] = None,
222241
) -> Iterable[Path]:
223-
root = self.workspace_root
242+
root = self.allowed_dir or self.workspace_root
224243
ex_patterns = list(exclude or [])
225244
count = 0
226245
for p in root.rglob("*"):
227246
try:
228247
if not p.is_file():
229248
continue
230-
if enforce_workspace and not self.is_within_workspace(p):
249+
if enforce_workspace and not self.is_within_scope(p):
231250
continue
232251
rel = p.relative_to(root).as_posix()
233252
if ex_patterns and any(fnmatch.fnmatch(rel, ex) for ex in ex_patterns):
@@ -263,14 +282,14 @@ def iter_project_files(
263282
return
264283
except Exception:
265284
key = None
266-
root = self.workspace_root
285+
root = self.allowed_dir or self.workspace_root
267286
matches: list[Path] = []
268287
count = 0
269288
for p in root.rglob("*"):
270289
try:
271290
if not p.is_file():
272291
continue
273-
if not self.is_within_workspace(p):
292+
if not self.is_within_scope(p):
274293
continue
275294
rel = p.relative_to(root).as_posix()
276295
if exc and any(fnmatch.fnmatch(rel, ex) for ex in exc):

0 commit comments

Comments
 (0)