Metadata-Version: 2.1
Name: holster-scan
Version: 0.0.1
Summary: Local-first scanner for hallucinated and typosquatted package imports.
Home-page: https://nautaai.com
Author: Nauta AI
License: UNKNOWN
Platform: UNKNOWN
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Holster Scan v0

Local-first scanner for AI-hallucinated, typosquatted, and slopsquatted package imports.

This v0 is the packaged form of the validated `probe-a-slopsquat` detector. It is not generic SAST, dependency vulnerability scanning, or license analysis. It is meant to catch package names that look like LLM-invented helper libraries, typo-near-misses of popular packages, or suspicious namespace/package-family abuse before they land in a repo or CI run.

## What It Catches

- Typos near popular packages, such as `reqeusts`, `numppy`, or `pytoch`.
- LLM-style invented helpers, such as `numpy-utils`, `openai-tools`, or `langchain-utils`.
- Suspicious namespace/package-family names near popular Python and JavaScript packages.
- Published but stale/abandoned look-alikes when the PyPI registry and maintenance oracle can confirm they are not actively maintained.

## What It Does Not Do

- It does not replace Snyk, Semgrep, Bandit, CodeQL, pip-audit, or npm audit.
- It does not prove a package is safe.
- It does not phone home or send telemetry.
- It does not query npm in v0. JavaScript/TypeScript scanning uses the name-pattern tier and declared dependency parsing only.

## Install

```bash
pip install -e .
```

The package and CLI name are locked as `holster-scan`.

## CLI Usage

```bash
holster-scan <path>
holster-scan <path> --format json
holster-scan <path> --format sarif > holster-scan.sarif
holster-scan <path> --fail-on medium
holster-scan <path> --offline
```

`holster-scan scan <path>` is also accepted for CI/action compatibility.

Exit codes:

- `0`: no findings at or above the configured fail threshold.
- `1`: findings at or above the configured fail threshold.
- `2`: CLI usage/path error.

## Config

Create `.holster.yml` at the repo root:

```yaml
allow:
  - torch_mlu
  - torch_musa
  - tree
  - transformers_old
  - internal_*
fail_on: high
registry: on
```

`allow` accepts names or shell-style globs. This is the v0 escape hatch for private indexes, vendor-index hardware backends, internal packages, and import-name-to-distribution gaps.

`registry: off` disables live PyPI existence/maintenance checks. `--offline` does the same for a single CLI run.

Offline behavior is intentionally conservative: unknown existence is not silently suppressed.

## GitHub Action

Use this repository as an action after checkout:

```yaml
name: Holster Scan

on:
  pull_request:
  push:

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: OWNER/REPO@v0
        with:
          path: "."
          fail-on: "high"
          upload-sarif: "true"
```

For a local repo checkout during development, install and run:

```bash
pip install -e .
holster-scan . --format sarif > holster-scan.sarif
```

## Network Behavior

Python scanning can use PyPI JSON as a registry oracle:

- confirmed exists: suppresses real-but-undeclared packages unless a stronger stale/confusable rule applies.
- confirmed 404: keeps the package eligible for flagging.
- unknown/network error/offline: does not suppress.

JavaScript/TypeScript scanning does not query npm in v0. It extracts `import`, `export ... from`, dynamic `import()`, and `require()` package names, reads `package.json` plus `package-lock.json`, and applies the same name-pattern detector with registry lookups disabled for that ecosystem.


