Authored-By: OpenAI (GPT-5) <noreply@openai.com>
This commit is contained in:
@@ -136,7 +136,7 @@ provisional cache policy or a pointer to a missing immutable target.
|
||||
|
||||
Artifact input directories must be pairwise disjoint after filesystem
|
||||
resolution. Publication stops before build or upload if one contains another or
|
||||
escapes the repository. Descendant symlinks are also rejected, preventing
|
||||
escapes the repository. Symlinked roots, components, and descendants are also rejected, preventing
|
||||
protected input from entering a public artifact through dereference. Split
|
||||
storage endpoints are pinned to Garage, and each website
|
||||
authority is derived from its bucket; a site cannot expose an arbitrary backend.
|
||||
|
||||
+8
-2
@@ -204,12 +204,18 @@ def s3_sync(artifact, route, site_dir, credential_env_names=None):
|
||||
# so a fresh upload always carries the right MIME type.
|
||||
specific_paths = [rule["path"] for rule in artifact["cache_rules"] if rule["path"]]
|
||||
default_filters = [arg for path in specific_paths for arg in ("--exclude", f"{path}/*")]
|
||||
# A move from a nested route to `/` makes the old partition fall inside the
|
||||
# new sync scope. Preserve declared immutable subtrees at every retired
|
||||
# prefix without enumerating the bucket.
|
||||
retired_immutable_filters = [
|
||||
arg for path in immutable_paths for arg in ("--exclude", f"*/{path}/*")
|
||||
]
|
||||
run(["aws", "--endpoint-url", endpoint, "s3", "cp", f"{html_dir}/", destination,
|
||||
"--recursive", "--only-show-errors", "--cache-control", default_cache,
|
||||
*default_filters, *exclude_args], env=aws_env)
|
||||
*default_filters, *retired_immutable_filters, *exclude_args], env=aws_env)
|
||||
run(["aws", "--endpoint-url", endpoint, "s3", "sync", f"{html_dir}/", destination,
|
||||
"--delete", "--only-show-errors", "--cache-control", default_cache,
|
||||
*default_filters, *exclude_args], env=aws_env)
|
||||
*default_filters, *retired_immutable_filters, *exclude_args], env=aws_env)
|
||||
for rule in artifact["cache_rules"]:
|
||||
if not rule["path"]:
|
||||
continue
|
||||
|
||||
+9
-1
@@ -432,7 +432,15 @@ def validate_artifact_inputs(site_dir, cfg):
|
||||
root = Path(site_dir).resolve()
|
||||
sources = []
|
||||
for artifact in cfg["artifacts"]:
|
||||
source = (root / artifact["content_dir"]).resolve()
|
||||
declared = root
|
||||
for component in Path(artifact["content_dir"]).parts:
|
||||
declared /= component
|
||||
if declared.is_symlink():
|
||||
raise ConfigError(
|
||||
f"artifact {artifact['name']} content_dir contains symlink component: "
|
||||
f"{declared.relative_to(root)}"
|
||||
)
|
||||
source = declared.resolve()
|
||||
if source != root and root not in source.parents:
|
||||
raise ConfigError(
|
||||
f"artifact {artifact['name']} content_dir resolves outside the repository"
|
||||
|
||||
@@ -230,6 +230,16 @@ class ConfigContractTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ConfigError, "build input contains symlink"):
|
||||
validate_artifact_inputs(root, cfg)
|
||||
|
||||
def test_artifact_root_symlink_is_rejected_before_resolution(self):
|
||||
cfg = normalize_site_config(self.raw, "baseline.fritzlab.net")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
(root / "dist-real").mkdir()
|
||||
(root / "dist").symlink_to(root / "dist-real")
|
||||
(root / "portal" / "build").mkdir(parents=True)
|
||||
with self.assertRaisesRegex(ConfigError, "content_dir contains symlink component"):
|
||||
validate_artifact_inputs(root, cfg)
|
||||
|
||||
|
||||
class GenerationTests(unittest.TestCase):
|
||||
def render(self, raw):
|
||||
@@ -417,6 +427,7 @@ class PublishingTests(unittest.TestCase):
|
||||
rendered = [" ".join(command) for command, _ in commands]
|
||||
self.assertIn("s3://baseline-dist/dist/", rendered[0])
|
||||
self.assertIn("releases/*", rendered[0])
|
||||
self.assertIn("*/releases/*", rendered[0])
|
||||
self.assertNotIn("--delete", rendered[0])
|
||||
self.assertIn("--delete", rendered[1])
|
||||
self.assertTrue(all("s3://baseline-dist/dist/" in command for command in rendered[1:]))
|
||||
|
||||
Reference in New Issue
Block a user