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
24 changes: 20 additions & 4 deletions .github/workflows/validate-plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,16 @@ jobs:
run: |
set -o pipefail
echo "Validating changed plugins: $CHANGED_PLUGINS"
read -ra PLUGINS_ARRAY <<< "$CHANGED_PLUGINS"
./scripts/validate-plugin-structure.sh "${PLUGINS_ARRAY[@]}" 2>&1 | tee /tmp/structure-validation.log
echo "DEBUG: Length of CHANGED_PLUGINS: ${#CHANGED_PLUGINS}"
echo "DEBUG: CHANGED_PLUGINS (hex): $(echo -n "$CHANGED_PLUGINS" | od -A n -t x1)"
if [[ -n "$CHANGED_PLUGINS" ]]; then
IFS=' ' read -r -a PLUGINS_ARRAY <<< "$CHANGED_PLUGINS"
echo "DEBUG: Array length: ${#PLUGINS_ARRAY[@]}"
echo "DEBUG: Array contents: ${PLUGINS_ARRAY[@]}"
./scripts/validate-plugin-structure.sh "${PLUGINS_ARRAY[@]}" 2>&1 | tee /tmp/structure-validation.log
else
echo "DEBUG: CHANGED_PLUGINS is empty, skipping validation"
fi

- name: Validate marketplace.json
id: marketplace
Expand All @@ -91,8 +99,16 @@ jobs:
run: |
set -o pipefail
echo "Validating marketplace entries for changed plugins: $CHANGED_PLUGINS"
read -ra PLUGINS_ARRAY <<< "$CHANGED_PLUGINS"
./scripts/validate-marketplace.sh "${PLUGINS_ARRAY[@]}" 2>&1 | tee /tmp/marketplace-validation.log
echo "DEBUG: Length of CHANGED_PLUGINS: ${#CHANGED_PLUGINS}"
echo "DEBUG: CHANGED_PLUGINS (hex): $(echo -n "$CHANGED_PLUGINS" | od -A n -t x1)"
if [[ -n "$CHANGED_PLUGINS" ]]; then
IFS=' ' read -r -a PLUGINS_ARRAY <<< "$CHANGED_PLUGINS"
echo "DEBUG: Array length: ${#PLUGINS_ARRAY[@]}"
echo "DEBUG: Array contents: ${PLUGINS_ARRAY[@]}"
./scripts/validate-marketplace.sh "${PLUGINS_ARRAY[@]}" 2>&1 | tee /tmp/marketplace-validation.log
else
echo "DEBUG: CHANGED_PLUGINS is empty, skipping validation"
fi

- name: Log in to Azure
if: steps.changed-files.outputs.has_components == 'true'
Expand Down
15 changes: 13 additions & 2 deletions scripts/lib/path-sanitization.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ sanitize_plugin_path() {

# Reject paths containing null bytes, newlines, or carriage returns
if [[ "$arg" =~ $'\0' ]] || [[ "$arg" =~ $'\n' ]] || [[ "$arg" =~ $'\r' ]]; then
echo "ERROR: Path contains invalid characters (null/newline/carriage return)" >&2
return 1
fi

Expand Down Expand Up @@ -77,11 +78,13 @@ sanitize_plugin_path() {
plugin_name="$arg"
else
# Invalid format or potentially dangerous pattern
echo "ERROR: Path format invalid. Expected 'plugins/name' or 'name', got: '$arg'" >&2
return 1
fi

# Validate plugin name contains only safe characters
if [[ ! "$plugin_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "ERROR: Plugin name contains invalid characters: '$plugin_name'" >&2
return 1
fi

Expand All @@ -90,20 +93,28 @@ sanitize_plugin_path() {

# Verify the path exists and is a directory
if [[ ! -d "$plugin_path" ]]; then
echo "ERROR: Plugin directory does not exist: $plugin_path" >&2
return 1
fi

# Use realpath to resolve any symlinks and get canonical path
local canonical_path
canonical_path="$(cd "$plugin_path" && pwd 2>/dev/null)" || return 1
if ! canonical_path="$(cd "$plugin_path" && pwd 2>/dev/null)"; then
echo "ERROR: Failed to resolve canonical path for: $plugin_path" >&2
return 1
fi

# Verify the resolved path is still under plugins_dir
local canonical_plugins_dir
canonical_plugins_dir="$(cd "$plugins_dir" && pwd 2>/dev/null)" || return 1
if ! canonical_plugins_dir="$(cd "$plugins_dir" && pwd 2>/dev/null)"; then
echo "ERROR: Failed to resolve plugins directory: $plugins_dir" >&2
return 1
fi

if [[ "$canonical_path" != "$canonical_plugins_dir" ]] && \
[[ ! "$canonical_path" =~ ^"$canonical_plugins_dir"/ ]]; then
# Path escapes the plugins directory - reject it
echo "ERROR: Path escapes plugins directory (security check failed)" >&2
return 1
fi

Expand Down
8 changes: 7 additions & 1 deletion scripts/validate-marketplace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,21 @@ main() {
# Build list of plugins to validate
local target_plugins=()
if [[ $# -gt 0 ]]; then
echo "DEBUG: Received $# argument(s): $*"
# Arguments provided - extract plugin names
for arg in "$@"; do
echo "DEBUG: Processing argument: '$arg'"
# Use shared sanitization function to safely parse plugin path
local sanitized_path
if sanitized_path=$(sanitize_plugin_path "$arg" "$REPO_ROOT/plugins" 2>/dev/null); then
if sanitized_path=$(sanitize_plugin_path "$arg" "$REPO_ROOT/plugins" 2>&1); then
# Extract just the plugin name from the full path
local plugin_name
plugin_name=$(basename "$sanitized_path")
echo "DEBUG: โœ“ Sanitized to: '$sanitized_path' โ†’ plugin name: '$plugin_name'"
target_plugins+=("$plugin_name")
else
echo "DEBUG: โœ— Failed to sanitize argument '$arg'"
echo "DEBUG: Output from sanitize_plugin_path: $sanitized_path"
fi
done

Expand Down
8 changes: 7 additions & 1 deletion scripts/validate-plugin-structure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,17 @@ main() {

# If arguments provided, validate only those plugins
if [[ $# -gt 0 ]]; then
echo "DEBUG: Received $# argument(s): $*"
for arg in "$@"; do
echo "DEBUG: Processing argument: '$arg'"
# Use shared sanitization function to safely parse plugin path
local sanitized_path
if sanitized_path=$(sanitize_plugin_path "$arg" "$PLUGINS_DIR" 2>/dev/null); then
if sanitized_path=$(sanitize_plugin_path "$arg" "$PLUGINS_DIR" 2>&1); then
echo "DEBUG: โœ“ Sanitized to: '$sanitized_path'"
plugins+=("$sanitized_path")
else
echo "DEBUG: โœ— Failed to sanitize argument '$arg'"
echo "DEBUG: Output from sanitize_plugin_path: $sanitized_path"
fi
done

Expand Down