Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.24.0
title: PromptShield
emoji: π‘οΈ
colorFrom: blue
colorTo: gray
sdk: gradio
app_file: app.py
pinned: false
sdk_version: 6.20.0
PromptShield
A lightweight security layer that detects prompt-injection / jailbreak attempts before they reach an LLM-powered application. Built as a standalone, deployable API + demo, and designed to sit in front of existing LLM apps (tested here against Consent Guardian and Prof-Scope-AI).
How it works
User prompt -> Preprocessing -> Feature extraction -> Detection engine -> Risk gate
(TF-IDF + heuristics) (ML classifier)
|
Allow ------+------ Block
(forward) (log + alert)
- Feature extraction: TF-IDF over the prompt text, combined with
hand-crafted heuristic signals (presence of override phrases like
"ignore previous instructions", imperative-mood sentence starts,
suspicious formatting markers like
###SYSTEM). - Detection engine: a Random Forest classifier (selected over Logistic Regression for higher recall on the injection class -- in security, a missed attack is worse than a false alarm).
- Risk gate: prompts scoring above 0.5 probability are blocked and logged; below that, they're forwarded to the downstream app.
Results
| Test | Accuracy | Notes |
|---|---|---|
| Held-out test split (same template distribution) | ~99-100% | Expected to be inflated -- same phrasing patterns as training |
| Hand-written novel prompts (out-of-distribution) | 12/12 (100%) | More honest signal -- zero overlap with training templates, confidence scores realistically in the 0.4-0.8 range rather than saturated at 0/1 |
Important honesty note for your write-up: the training data here is
template-generated (see src/build_dataset.py) because this environment
couldn't reach Hugging Face Hub to pull the public deepset/prompt-injections
dataset. The novel-prompt test is a genuine generalization check, but a
larger, real-world dataset (see below) would make the results far more
defensible for a research write-up or professor-facing project.
Upgrading to a real dataset (do this on Colab / your own machine)
This sandbox can't reach huggingface.co, so run this step separately:
# pip install datasets
from src.build_dataset import load_real_dataset
load_real_dataset() # pulls deepset/prompt-injections, saves to data/prompts_real.csv
Then retrain with train.py pointed at data/prompts_real.csv instead.
Consider also mixing in your synthetic data for extra coverage of
paraphrase variety.
Project structure
PromptShield/
βββ app.py # FastAPI + Gradio, deployment entry point
βββ requirements.txt
βββ data/
β βββ prompts.csv # synthetic training data
βββ models/
β βββ classifier.joblib
β βββ vectorizer.joblib
βββ src/
βββ build_dataset.py # dataset generation (+ real-dataset loader)
βββ features.py # TF-IDF + heuristic feature extraction
βββ train.py # training + evaluation
βββ novel_test.py # honest out-of-distribution test
Running locally
pip install -r requirements.txt
python src/build_dataset.py # generate data
python src/train.py # train + evaluate
python src/novel_test.py # sanity-check generalization
python app.py # launch API + demo at localhost:7860
Deploying to Hugging Face Spaces
- Create a new Space, SDK = Gradio.
- Upload this entire folder (or push via git).
- Spaces auto-detects
app.pyas the entry point -- no extra config needed. - Your guardrail is now live at
https://huggingface.co/spaces/<you>/<space-name>, with the/checkendpoint callable from any other app.
Calling it from another app (e.g. Consent Guardian, Prof-Scope-AI)
This Space uses Gradio's built-in API (no separate FastAPI server -- this avoids a port conflict with Hugging Face's ZeroGPU hardware, which is the only free tier available on new accounts).
Option A: raw HTTP (two-step call -- POST then GET the result)
import requests
# Step 1: submit the prompt
resp = requests.post(
"https://<your-space>.hf.space/gradio_api/call/check",
json={"data": [user_input]}
)
event_id = resp.json()["event_id"]
# Step 2: fetch the result
result = requests.get(
f"https://<your-space>.hf.space/gradio_api/call/check/{event_id}"
)
# result.text looks like: 'event: complete\ndata: ["blocked", 0.945]\n'
Option B: gradio_client (simpler, recommended)
# pip install gradio_client
from gradio_client import Client
client = Client("https://<your-space>.hf.space")
decision, risk_score = client.predict(user_input, api_name="/check")
if decision == "blocked":
# reject / log / alert instead of forwarding to the LLM
...