mirror of
https://github.com/github/spec-kit.git
synced 2026-04-02 18:53:09 +00:00
fix: lexical symlink containment, assert project_root consistency
- uninstall() now uses os.path.normpath for lexical containment check instead of resolve(), so in-project symlinks pointing outside are still properly removed - setup() asserts manifest.project_root matches the passed project_root to prevent path mismatches between file operations and manifest recording
This commit is contained in:
@@ -120,6 +120,11 @@ class IntegrationBase(ABC):
|
|||||||
return created
|
return created
|
||||||
|
|
||||||
project_root_resolved = project_root.resolve()
|
project_root_resolved = project_root.resolve()
|
||||||
|
if manifest.project_root != project_root_resolved:
|
||||||
|
raise ValueError(
|
||||||
|
f"manifest.project_root ({manifest.project_root}) does not match "
|
||||||
|
f"project_root ({project_root_resolved})"
|
||||||
|
)
|
||||||
subdir = self.config.get("commands_subdir", "commands")
|
subdir = self.config.get("commands_subdir", "commands")
|
||||||
dest = (project_root / folder / subdir).resolve()
|
dest = (project_root / folder / subdir).resolve()
|
||||||
# Ensure destination stays within the project root
|
# Ensure destination stays within the project root
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -147,10 +148,11 @@ class IntegrationManifest:
|
|||||||
# Use non-resolved path for deletion so symlinks themselves
|
# Use non-resolved path for deletion so symlinks themselves
|
||||||
# are removed, not their targets.
|
# are removed, not their targets.
|
||||||
path = root / rel
|
path = root / rel
|
||||||
# Validate containment via the resolved path
|
# Validate containment lexically (without following symlinks)
|
||||||
|
# by collapsing .. segments via Path resolution on the string parts.
|
||||||
try:
|
try:
|
||||||
resolved = path.resolve()
|
normed = Path(os.path.normpath(path))
|
||||||
resolved.relative_to(root)
|
normed.relative_to(root)
|
||||||
except (ValueError, OSError):
|
except (ValueError, OSError):
|
||||||
continue
|
continue
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
|
|||||||
Reference in New Issue
Block a user