diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index 501759323..1dfe21af9 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -716,6 +716,12 @@ func decodeIfBase64Wasm(content []byte) []byte { return content } +// isURLPath checks if a path is an HTTP or HTTPS URL +func isURLPath(path string) bool { + scheme, _ := RefParts(path) + return scheme == httpScheme || scheme == httpsScheme +} + func loadPolicyScript(spec *v1.PolicySpecV2, basePath string) ([]byte, error) { var content []byte var err error @@ -723,8 +729,14 @@ func loadPolicyScript(spec *v1.PolicySpecV2, basePath string) ([]byte, error) { case *v1.PolicySpecV2_Embedded: content = []byte(source.Embedded) case *v1.PolicySpecV2_Path: - // path relative to policy folder - scriptPath := filepath.Join(filepath.Dir(basePath), source.Path) + var scriptPath string + // If the path is a URL, use it directly. Otherwise, resolve it relative to basePath + if isURLPath(source.Path) { + scriptPath = source.Path + } else { + // path relative to policy folder + scriptPath = filepath.Join(filepath.Dir(basePath), source.Path) + } content, err = blob.LoadFileOrURL(scriptPath) if err != nil { return nil, fmt.Errorf("loading policy content: %w", err) @@ -747,8 +759,14 @@ func loadLegacyPolicyScript(spec *v1.PolicySpec, basePath string) ([]byte, error case *v1.PolicySpec_Embedded: content = []byte(source.Embedded) case *v1.PolicySpec_Path: - // path relative to policy folder - scriptPath := filepath.Join(filepath.Dir(basePath), source.Path) + var scriptPath string + // If the path is a URL, use it directly. Otherwise, resolve it relative to basePath + if isURLPath(source.Path) { + scriptPath = source.Path + } else { + // path relative to policy folder + scriptPath = filepath.Join(filepath.Dir(basePath), source.Path) + } content, err = blob.LoadFileOrURL(scriptPath) if err != nil { return nil, fmt.Errorf("loading policy content: %w", err) diff --git a/pkg/policies/policies_test.go b/pkg/policies/policies_test.go index 460717702..b976d77ac 100644 --- a/pkg/policies/policies_test.go +++ b/pkg/policies/policies_test.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1248,3 +1248,54 @@ func loadStatement(file string, s *suite.Suite) *intoto.Statement { return &statement } + +func (s *testSuite) TestIsURLPath() { + cases := []struct { + name string + path string + expected bool + }{ + { + name: "http URL", + path: "http://example.com/policy.rego", + expected: true, + }, + { + name: "https URL", + path: "https://example.com/policy.rego", + expected: true, + }, + { + name: "relative file path", + path: "policy.rego", + expected: false, + }, + { + name: "absolute file path", + path: "/absolute/path/policy.rego", + expected: false, + }, + { + name: "file scheme", + path: "file:///path/to/policy.rego", + expected: false, + }, + { + name: "chainloop scheme", + path: "chainloop://provider/policy", + expected: false, + }, + { + name: "empty path", + path: "", + expected: false, + }, + } + + for _, tc := range cases { + s.Run(tc.name, func() { + result := isURLPath(tc.path) + s.Equal(tc.expected, result) + }) + } +}