Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions docs/examples/policies/_testutils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,35 @@ test_policy_eval() {
actual_result="pass" # No violations = test should pass
fi
else
actual_result="fail" # Command failed
actual_result="failed_eval" # Command failed to execute
fi

# Compare with expected result
if [ "$actual_result" = "$expected_result" ]; then
if [ "$expected_result" = "pass" ]; then
echo -e "${GREEN}✓ PASSED${NC}"
else
echo -e "${GREEN}✓ FAILED (as expected)${NC}"
fi
case "$expected_result" in
"pass")
echo -e "${GREEN}✓ PASSED${NC}"
;;
"fail")
echo -e "${GREEN}✓ FAILED (as expected)${NC}"
;;
"failed_eval")
echo -e "${GREEN}✓ EVAL FAILED (as expected)${NC}"
;;
esac
((TESTS_PASSED++))
else
if [ "$expected_result" = "pass" ]; then
echo -e "${RED}✗ FAILED (expected to pass but failed)${NC}"
else
echo -e "${RED}✗ PASSED (expected to fail but passed)${NC}"
fi
case "$expected_result" in
"pass")
echo -e "${RED}✗ FAILED (expected to pass but $actual_result)${NC}"
;;
"fail")
echo -e "${RED}✗ PASSED (expected to fail but $actual_result)${NC}"
;;
"failed_eval")
echo -e "${RED}✗ PASSED (expected eval failure but $actual_result)${NC}"
;;
esac

# Show skip reason if policy wasn't executed
if [ -n "${skip_reason:-}" ]; then
Expand Down
66 changes: 66 additions & 0 deletions docs/examples/policies/http-hostname-validation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# HTTP Hostname Validation Example

Demonstrates how to make HTTP requests to external APIs from Chainloop policies while maintaining security through hostname allowlisting.

## What This Policy Does

This policy is just an example which validates the Chainloop platform version by making HTTP requests to its external info API.

It demonstrates:

- **HTTP API Integration** - Makes requests to `https://app.chainloop.dev/api/info`
- **Response validation** - Compares API response against expected version (configurable)
- **Hostname Security** - Requires explicit hostname allowlisting for HTTP requests
- **Error Handling** - Gracefully handles network failures and API errors

## Policy Parameters

| Parameter | Description | Required | Default | Example |
|-----------|-------------|----------|---------|---------|
| `expected_version` | Expected platform version to validate against | ❌ No | `1.2.3` | `v0.256.0`, `2.0.0` |

## The HTTP Security Challenge

By default, Chainloop policies **block all HTTP requests** for security reasons. This policy will fail with:

```
ERR evaluating policy: unallowed host: app.chainloop.dev
```

## Solution: Hostname Allowlisting

Use the `--allowed-hostnames` flag to explicitly allow specific hostnames:

```bash
chainloop policy develop eval \
--policy policy.yaml \
--material testdata/empty.json \
--kind EVIDENCE \
--allowed-hostnames app.chainloop.dev
```

## Using in Workflow Contracts

Add this policy to your workflow contract:

```yaml
apiVersion: workflowcontract.chainloop.dev/v1
kind: WorkflowContract
metadata:
name: platform-validation-workflow
spec:
materials:
- type: EVIDENCE
name: platform-check

policies:
- ref: ./http-hostname-validation/policy.yaml
with:
expected_version: "2.0.0" # Optional, defaults to "1.2.3"
```

**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.

## Development & Testing

See [test.sh](test.sh) for the test cases.
66 changes: 66 additions & 0 deletions docs/examples/policies/http-hostname-validation/policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
apiVersion: workflowcontract.chainloop.dev/v1
kind: Policy
metadata:
name: http-hostname-validation
description: Validates Chainloop platform version by making HTTP requests to external APIs
spec:
inputs:
- name: expected_version
description: Expected platform version to validate against
default: "1.2.3"
policies:
- kind: EVIDENCE
embedded: |
package main

import rego.v1

################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
"ignore": ignore,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

default ignore := false

########################################
# EO Common section, custom code below #
########################################
# Validates if the input is valid and can be understood by this policy
valid_input := true

# Make HTTP request to Chainloop API
api_response := http.send({
"method": "GET",
"url": "https://app.chainloop.dev/api/info",
"cache": true,
})

# Extract platform version from API response
platform_version := api_response.body.platform.version

# Get expected version from input (default handled by engine)
expected_version := input.args.expected_version

violations contains msg if {
valid_input
api_response.status_code == 200
platform_version != expected_version
msg := sprintf("Platform version violation: expected '%s', got '%s'", [expected_version, platform_version])
}
53 changes: 53 additions & 0 deletions docs/examples/policies/http-hostname-validation/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/bash

# HTTP Hostname Validation Policy Tests
# This file demonstrates the --allowed-hostnames flag functionality
source ../_testutils.sh

# Initialize test framework
init_tests

# Verify required files exist
verify_files "policy.yaml" "testdata/empty.json"

# Get current platform version from the API to perform the test passing the right version
get_platform_version() {
if command -v curl &> /dev/null; then
curl -s https://app.chainloop.dev/api/info | grep -o '"version":"[^"]*"' | cut -d'"' -f4
elif command -v wget &> /dev/null; then
wget -qO- https://app.chainloop.dev/api/info | grep -o '"version":"[^"]*"' | cut -d'"' -f4
else
echo "v0.256.0" # fallback version
fi
}

test_section "Policy Validation"
test_policy_lint "policy.yaml"

test_policy_eval "No Allowed Hostnames - Should Fail the evaluation" "failed_eval" \
--kind EVIDENCE \
--material testdata/empty.json

test_policy_eval "With Wrong Allowed Hostname - Should Fail" "failed_eval" \
--kind EVIDENCE \
--material testdata/empty.json \
--allowed-hostnames example.com

test_policy_eval "With Correct Allowed Hostname should run evaluation but fail because of version mismatch" "fail" \
--kind EVIDENCE \
--material testdata/empty.json \
--allowed-hostnames app.chainloop.dev

echo "Fetching current platform version..."
CURRENT_VERSION=$(get_platform_version)
echo "Current platform version: $CURRENT_VERSION"
echo ""

test_policy_eval "Custom Expected Version (matching current platform)" "pass" \
--kind EVIDENCE \
--material testdata/empty.json \
--allowed-hostnames app.chainloop.dev \
--input expected_version=$CURRENT_VERSION

# Print test summary and exit
test_summary
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading