diff --git a/.github/tests/deploy-static-lock-validator.test.rb b/.github/tests/deploy-static-lock-validator.test.rb index 87e1ef4..917c8a8 100644 --- a/.github/tests/deploy-static-lock-validator.test.rb +++ b/.github/tests/deploy-static-lock-validator.test.rb @@ -259,6 +259,9 @@ "unknown private manifest package" => ["@cellarnode/internal-secrets", "1.0.0", false], "aliased unknown private manifest package" => ["hidden-package", "npm:@cellarnode/internal-secrets@1.0.0", false], "allowed private manifest package" => ["@cellarnode/ui", "1.0.0", true], + "frozen renderer registry alias" => ["@cellarnode/ui-renderer-0-154", "npm:@cellarnode/ui@0.154.0", true], + "frozen renderer alias to public target" => ["@cellarnode/ui-renderer-0-154", "npm:leftpad@1.0.0", false], + "allowed alias with path traversal version" => ["@cellarnode/ui", "npm:@cellarnode/ui@0.154.0/../../evil", false], "allowed public registry range" => ["public-package", "^1.0.0", true], "aliased public manifest package" => ["@cellarnode/ui", "npm:evil-package@1.0.0", false], "git manifest package" => ["public-package", "git+https://evil.example/package.git", false], diff --git a/.github/workflows/deploy-static-website.yaml b/.github/workflows/deploy-static-website.yaml index 2d5b692..9935b30 100644 --- a/.github/workflows/deploy-static-website.yaml +++ b/.github/workflows/deploy-static-website.yaml @@ -190,7 +190,14 @@ jobs: finance i18n ui + ui-renderer-0-154 ].freeze + # A version-pinned npm: alias is registry-sourced, but only aliases + # that TARGET an allowed @cellarnode package are exempt from the + # protocol check (e.g. @cellarnode/ui-renderer-0-154 -> + # npm:@cellarnode/ui@0.154.0, the frozen e-label public renderer, + # CEL-1522). An npm: alias pointing anywhere else still fails. + allowed_registry_alias = /\Anpm:@cellarnode\/(?:#{Regexp.union(allowed_private_packages).source})@[^\s\/\\]+\z/ failures = [] manifests = [] Find.find(".") do |path| @@ -221,8 +228,12 @@ jobs: value.scan(private_package).flatten.each do |package| failures << "#{path}:#{keys.join('.')}" unless allowed_private_packages.include?(package.downcase) end + # An allowed registry alias is exempt from BOTH source checks: its + # scope slash would otherwise trip the path heuristic. if keys.any? { |key| dependency_sections.include?(key) } && - (protocol_dependency_source.match?(value) || path_or_archive_dependency_source.match?(value)) + !allowed_registry_alias.match?(value) && + (protocol_dependency_source.match?(value) || + path_or_archive_dependency_source.match?(value)) failures << "#{path}:#{keys.join('.')}" end end @@ -261,6 +272,7 @@ jobs: finance i18n ui + ui-renderer-0-154 ].freeze failures = [] validate_private_packages = lambda do |text, keys|