Skip to content

Commit 531fe76

Browse files
authored
example(policies): add http-allowed-hostnames example (#2342)
Signed-off-by: Miguel Martinez <[email protected]>
1 parent a3159b7 commit 531fe76

File tree

5 files changed

+209
-11
lines changed

5 files changed

+209
-11
lines changed

docs/examples/policies/_testutils.sh

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,23 +151,35 @@ test_policy_eval() {
151151
actual_result="pass" # No violations = test should pass
152152
fi
153153
else
154-
actual_result="fail" # Command failed
154+
actual_result="failed_eval" # Command failed to execute
155155
fi
156156

157157
# Compare with expected result
158158
if [ "$actual_result" = "$expected_result" ]; then
159-
if [ "$expected_result" = "pass" ]; then
160-
echo -e "${GREEN}✓ PASSED${NC}"
161-
else
162-
echo -e "${GREEN}✓ FAILED (as expected)${NC}"
163-
fi
159+
case "$expected_result" in
160+
"pass")
161+
echo -e "${GREEN}✓ PASSED${NC}"
162+
;;
163+
"fail")
164+
echo -e "${GREEN}✓ FAILED (as expected)${NC}"
165+
;;
166+
"failed_eval")
167+
echo -e "${GREEN}✓ EVAL FAILED (as expected)${NC}"
168+
;;
169+
esac
164170
((TESTS_PASSED++))
165171
else
166-
if [ "$expected_result" = "pass" ]; then
167-
echo -e "${RED}✗ FAILED (expected to pass but failed)${NC}"
168-
else
169-
echo -e "${RED}✗ PASSED (expected to fail but passed)${NC}"
170-
fi
172+
case "$expected_result" in
173+
"pass")
174+
echo -e "${RED}✗ FAILED (expected to pass but $actual_result)${NC}"
175+
;;
176+
"fail")
177+
echo -e "${RED}✗ PASSED (expected to fail but $actual_result)${NC}"
178+
;;
179+
"failed_eval")
180+
echo -e "${RED}✗ PASSED (expected eval failure but $actual_result)${NC}"
181+
;;
182+
esac
171183

172184
# Show skip reason if policy wasn't executed
173185
if [ -n "${skip_reason:-}" ]; then
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# HTTP Hostname Validation Example
2+
3+
Demonstrates how to make HTTP requests to external APIs from Chainloop policies while maintaining security through hostname allowlisting.
4+
5+
## What This Policy Does
6+
7+
This policy is just an example which validates the Chainloop platform version by making HTTP requests to its external info API.
8+
9+
It demonstrates:
10+
11+
- **HTTP API Integration** - Makes requests to `https://app.chainloop.dev/api/info`
12+
- **Response validation** - Compares API response against expected version (configurable)
13+
- **Hostname Security** - Requires explicit hostname allowlisting for HTTP requests
14+
- **Error Handling** - Gracefully handles network failures and API errors
15+
16+
## Policy Parameters
17+
18+
| Parameter | Description | Required | Default | Example |
19+
|-----------|-------------|----------|---------|---------|
20+
| `expected_version` | Expected platform version to validate against | ❌ No | `1.2.3` | `v0.256.0`, `2.0.0` |
21+
22+
## The HTTP Security Challenge
23+
24+
By default, Chainloop policies **block all HTTP requests** for security reasons. This policy will fail with:
25+
26+
```
27+
ERR evaluating policy: unallowed host: app.chainloop.dev
28+
```
29+
30+
## Solution: Hostname Allowlisting
31+
32+
Use the `--allowed-hostnames` flag to explicitly allow specific hostnames:
33+
34+
```bash
35+
chainloop policy develop eval \
36+
--policy policy.yaml \
37+
--material testdata/empty.json \
38+
--kind EVIDENCE \
39+
--allowed-hostnames app.chainloop.dev
40+
```
41+
42+
## Using in Workflow Contracts
43+
44+
Add this policy to your workflow contract:
45+
46+
```yaml
47+
apiVersion: workflowcontract.chainloop.dev/v1
48+
kind: WorkflowContract
49+
metadata:
50+
name: platform-validation-workflow
51+
spec:
52+
materials:
53+
- type: EVIDENCE
54+
name: platform-check
55+
56+
policies:
57+
- ref: ./http-hostname-validation/policy.yaml
58+
with:
59+
expected_version: "2.0.0" # Optional, defaults to "1.2.3"
60+
```
61+
62+
**Note**: When running in production, the Control Plane manages hostname allowlisting through organization settings. The `--allowed-hostnames` flag is only for local development and testing.
63+
64+
## Development & Testing
65+
66+
See [test.sh](test.sh) for the test cases.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
apiVersion: workflowcontract.chainloop.dev/v1
2+
kind: Policy
3+
metadata:
4+
name: http-hostname-validation
5+
description: Validates Chainloop platform version by making HTTP requests to external APIs
6+
spec:
7+
inputs:
8+
- name: expected_version
9+
description: Expected platform version to validate against
10+
default: "1.2.3"
11+
policies:
12+
- kind: EVIDENCE
13+
embedded: |
14+
package main
15+
16+
import rego.v1
17+
18+
################################
19+
# Common section do NOT change #
20+
################################
21+
22+
result := {
23+
"skipped": skipped,
24+
"violations": violations,
25+
"skip_reason": skip_reason,
26+
"ignore": ignore,
27+
}
28+
29+
default skip_reason := ""
30+
31+
skip_reason := m if {
32+
not valid_input
33+
m := "invalid input"
34+
}
35+
36+
default skipped := true
37+
38+
skipped := false if valid_input
39+
40+
default ignore := false
41+
42+
########################################
43+
# EO Common section, custom code below #
44+
########################################
45+
# Validates if the input is valid and can be understood by this policy
46+
valid_input := true
47+
48+
# Make HTTP request to Chainloop API
49+
api_response := http.send({
50+
"method": "GET",
51+
"url": "https://app.chainloop.dev/api/info",
52+
"cache": true,
53+
})
54+
55+
# Extract platform version from API response
56+
platform_version := api_response.body.platform.version
57+
58+
# Get expected version from input (default handled by engine)
59+
expected_version := input.args.expected_version
60+
61+
violations contains msg if {
62+
valid_input
63+
api_response.status_code == 200
64+
platform_version != expected_version
65+
msg := sprintf("Platform version violation: expected '%s', got '%s'", [expected_version, platform_version])
66+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/bash
2+
3+
# HTTP Hostname Validation Policy Tests
4+
# This file demonstrates the --allowed-hostnames flag functionality
5+
source ../_testutils.sh
6+
7+
# Initialize test framework
8+
init_tests
9+
10+
# Verify required files exist
11+
verify_files "policy.yaml" "testdata/empty.json"
12+
13+
# Get current platform version from the API to perform the test passing the right version
14+
get_platform_version() {
15+
if command -v curl &> /dev/null; then
16+
curl -s https://app.chainloop.dev/api/info | grep -o '"version":"[^"]*"' | cut -d'"' -f4
17+
elif command -v wget &> /dev/null; then
18+
wget -qO- https://app.chainloop.dev/api/info | grep -o '"version":"[^"]*"' | cut -d'"' -f4
19+
else
20+
echo "v0.256.0" # fallback version
21+
fi
22+
}
23+
24+
test_section "Policy Validation"
25+
test_policy_lint "policy.yaml"
26+
27+
test_policy_eval "No Allowed Hostnames - Should Fail the evaluation" "failed_eval" \
28+
--kind EVIDENCE \
29+
--material testdata/empty.json
30+
31+
test_policy_eval "With Wrong Allowed Hostname - Should Fail" "failed_eval" \
32+
--kind EVIDENCE \
33+
--material testdata/empty.json \
34+
--allowed-hostnames example.com
35+
36+
test_policy_eval "With Correct Allowed Hostname should run evaluation but fail because of version mismatch" "fail" \
37+
--kind EVIDENCE \
38+
--material testdata/empty.json \
39+
--allowed-hostnames app.chainloop.dev
40+
41+
echo "Fetching current platform version..."
42+
CURRENT_VERSION=$(get_platform_version)
43+
echo "Current platform version: $CURRENT_VERSION"
44+
echo ""
45+
46+
test_policy_eval "Custom Expected Version (matching current platform)" "pass" \
47+
--kind EVIDENCE \
48+
--material testdata/empty.json \
49+
--allowed-hostnames app.chainloop.dev \
50+
--input expected_version=$CURRENT_VERSION
51+
52+
# Print test summary and exit
53+
test_summary
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

0 commit comments

Comments
 (0)