# How the resolve_input Node Normalizes Input Sources in SkillSpector

> Learn how SkillSpector resolve_input normalizes Git repos ZIPs URLs and local files into a standard directory while blocking SSRF and zip-slip attacks.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: internals
- Published: 2026-06-23

---

**The `resolve_input` node converts diverse skill sources—Git repositories, ZIP archives, URLs, or local files—into a standardized local directory path while enforcing security checks against SSRF and zip-slip attacks.**

The `resolve_input` node serves as the entry point for NVIDIA's SkillSpector analysis pipeline. Located in [`src/skillspector/nodes/resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/resolve_input.py), this node transforms whatever input the user provides into a predictable local directory structure that downstream nodes can scan. It handles the complexity of remote cloning, downloading, and extraction so the rest of the graph works with a normalized `skill_path`.

## Core Functionality and Data Flow

### State-Based Input Processing

The node accepts a `SkillspectorState` object (defined in [`src/skillspector/state.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/state.py)) containing an `input_path` field. According to the implementation in [`resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/resolve_input.py) lines 34-71, the function signature is:

```python
def resolve_input(state: SkillspectorState) -> dict[str, object]:

```

When `input_path` is present, the node instantiates an `InputHandler` and calls its `resolve()` method (lines 46-50). This handler determines whether the input is a Git URL, raw file URL, ZIP archive, single file, or existing directory.

### Return Value Structure

The node returns a dictionary containing two critical keys:

- ** `skill_path`**: The absolute path to the prepared directory ready for analysis
- ** `temp_dir_for_cleanup`**: The temporary directory path for post-analysis cleanup, or `None` if the input was already local

As implemented in lines 50-56 of [`resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/resolve_input.py), the node captures temporary directories created during resolution to ensure proper cleanup later.

## Input Type Resolution and Security Architecture

### Supported Source Types

The `InputHandler.resolve()` method in [`src/skillspector/input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/input_handler.py) (lines 89-119) categorizes inputs and performs specific operations:

- **Git URLs**: Shallow clone into a temporary directory
- **Raw file URLs**: Download and optionally unzip into a temp directory
- **ZIP files**: Safe extraction into a temporary workspace
- **Single files**: Copy into a temporary directory
- **Directories**: Resolve to absolute path without copying

### Security Safeguards

Before any filesystem operations, the handler enforces three security layers:

1. **Allowed Hosts Whitelist**: Only approved Git hosts (`github.com`, `gitlab.com`, `bitbucket.org`) and download sources (`raw.githubusercontent.com`, `huggingface.co`) are permitted
2. **SSRF Protection**: The `_is_private_ip` check (lines 61-78 in [`input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/input_handler.py)) resolves hostnames and rejects private, loopback, link-local, or reserved IP addresses
3. **Zip-Slip Mitigation**: Each entry in ZIP archives is validated to stay within the extraction directory before writing (lines 38-45 in [`input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/input_handler.py))

## Practical Usage Examples

### Direct Node Invocation

```python
from skillspector.state import SkillspectorState
from skillspector.nodes.resolve_input import resolve_input

# Scan a public Git repository

state = SkillspectorState({"input_path": "https://github.com/example/my-skill.git"})
result = resolve_input(state)

# Returns: {"skill_path": "/tmp/skillspector_abcdef/repo", 

#           "temp_dir_for_cleanup": "/tmp/skillspector_abcdef"}

```

### Handling Local Archives

```python

# Process a local ZIP file

state = SkillspectorState({"input_path": "/home/user/my_skill.zip"})
result = resolve_input(state)
print(result["skill_path"])

# Output: "/tmp/skillspector_12345/extracted"

```

### Pipeline Integration

When used within the full SkillSpector graph orchestrated by [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py), the node automatically handles the first transformation step:

```python
from skillspector.graph import run_graph

state = {"input_path": "https://huggingface.co/datasets/example_skill.zip"}
output = run_graph(state)  # Internally calls resolve_input first

print(output["skill_path"])

```

## Summary

- The `resolve_input` node in [`src/skillspector/nodes/resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/resolve_input.py) serves as the unified entry point for all skill sources in the SkillSpector pipeline
- It delegates actual resolution logic to the `InputHandler` class in [`src/skillspector/input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/input_handler.py)
- The node returns a normalized `skill_path` and tracks temporary directories via `temp_dir_for_cleanup` for proper resource management
- Security checks including host whitelisting, SSRF prevention, and zip-slip protection run before any filesystem modifications
- The implementation handles Git clones, URL downloads, ZIP extraction, and local directory resolution through a single consistent interface

## Frequently Asked Questions

### What happens if the input_path is missing or empty?

If the state lacks an `input_path` but contains a `skill_path`, the node normalizes the existing path to an absolute path using `Path(skill_path).resolve()` (lines 60-66 in [`resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/resolve_input.py)). If neither field is present, the node returns `{"skill_path": None, "temp_dir_for_cleanup": None}`, allowing downstream nodes like `build_context` to raise user-friendly error messages.

### How does SkillSpector prevent malicious ZIP files from overwriting system files?

The `InputHandler` implements zip-slip mitigation by validating each entry path in [`input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/input_handler.py) lines 38-45. Before extraction, it resolves the absolute path of each ZIP entry and verifies it remains within the intended extraction directory, preventing directory traversal attacks that attempt to write to parent directories or system paths.

### Can resolve_input handle private Git repositories or internal URLs?

The current implementation restricts remote sources to whitelisted hosts including `github.com`, `gitlab.com`, `bitbucket.org`, and specific download domains. As implemented in [`input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/input_handler.py) lines 61-78, the code performs SSRF checks that block private IP ranges, meaning internal or private Git repositories would require modification of the allowed hosts list or use of local directory inputs instead.

### Is the temporary directory automatically cleaned up after analysis?

The `resolve_input` node returns the temporary directory path via `temp_dir_for_cleanup` (lines 50-56 in [`resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/resolve_input.py)), but the cleanup responsibility lies with the graph orchestrator or the calling code. The node ensures the path is available for cleanup operations but does not perform the deletion itself, allowing downstream nodes to complete their analysis before resources are released.