init commit
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
.direnv
|
||||
.conda
|
||||
/lab2/*.pt
|
||||
*.safetensors
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.11
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim
|
||||
|
||||
RUN useradd -m labuser
|
||||
WORKDIR /workspace
|
||||
COPY pyproject.toml .
|
||||
RUN chown -R labuser:labuser /workspace
|
||||
USER labuser
|
||||
|
||||
RUN uv sync
|
||||
ENV PATH="/workspace/.venv/bin:${PATH}"
|
||||
|
||||
EXPOSE 8888
|
||||
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--NotebookApp.token=''"]
|
||||
|
||||
2
README.md
Normal file
2
README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Ai Security Issues Lab Code
|
||||
Each lab has its own directory
|
||||
47
features.py
Normal file
47
features.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import math
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def shannon_entropy(s: str) -> float:
|
||||
if not s:
|
||||
return 0.0
|
||||
counts = Counter(s)
|
||||
length = len(s)
|
||||
return -sum((c / length) * math.log2(c / length) for c in counts.values())
|
||||
|
||||
|
||||
def special_char_ratio(s: str) -> float:
|
||||
if not s:
|
||||
return 0.0
|
||||
specials = sum(1 for ch in s if not ch.isalnum() and not ch.isspace())
|
||||
return specials / len(s)
|
||||
|
||||
|
||||
def digit_ratio(s: str) -> float:
|
||||
if not s:
|
||||
return 0.0
|
||||
return sum(ch.isdigit() for ch in s) / len(s)
|
||||
|
||||
|
||||
def keyword_hit_count(s: str, keywords) -> int:
|
||||
low = s.lower()
|
||||
return sum(1 for kw in keywords if kw in low)
|
||||
|
||||
|
||||
def handcrafted_feature_matrix(texts, keywords):
|
||||
rows = []
|
||||
for t in texts:
|
||||
rows.append([
|
||||
len(t),
|
||||
shannon_entropy(t),
|
||||
special_char_ratio(t),
|
||||
digit_ratio(t),
|
||||
keyword_hit_count(t, keywords),
|
||||
])
|
||||
return np.array(rows, dtype=float)
|
||||
|
||||
|
||||
FEATURE_NAMES = ["length", "entropy", "special_char_ratio", "digit_ratio", "keyword_hits"]
|
||||
18
lab2/good_model.py
Normal file
18
lab2/good_model.py
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
import torch
|
||||
import os
|
||||
|
||||
class MaliciousPayload:
|
||||
def __reduce__(self):
|
||||
command = 'echo "\n[SECURITY WARNING] Arbitrary code executed successfully!\n"'
|
||||
return (os.system, (command,))
|
||||
|
||||
dummy_model_state = {
|
||||
"weight": torch.randn(3, 3),
|
||||
"bias": torch.randn(3),
|
||||
"exploit": MaliciousPayload()
|
||||
}
|
||||
|
||||
checkpoint_path = "compromised_model.pt"
|
||||
torch.save(dummy_model_state, checkpoint_path)
|
||||
print(f"Malicious checkpoint saved to {checkpoint_path}")
|
||||
11
lab2/load_model.py
Normal file
11
lab2/load_model.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
import torch
|
||||
|
||||
print("Attempting to load standard checkpoint (unrestricted)...")
|
||||
try:
|
||||
# In older versions of PyTorch, weights_only defaulted to False.
|
||||
# This will trigger the __reduce__ exploit instantly upon reading.
|
||||
loaded_data = torch.load("compromised_model.pt", weights_only=False)
|
||||
print("Model loaded successfully (but the exploit already ran!).")
|
||||
except Exception as e:
|
||||
print(f"Error occurred: {e}")
|
||||
19
lab2/safe_tensor_sample.py
Normal file
19
lab2/safe_tensor_sample.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
from safetensors.torch import save_file, load_file
|
||||
import torch
|
||||
|
||||
# 1. Clean data (safetensors strictly accepts ONLY a flat dictionary of tensors)
|
||||
clean_state_dict = {
|
||||
"layer1.weight": torch.randn(5, 5),
|
||||
"layer1.bias": torch.randn(5)
|
||||
}
|
||||
|
||||
# 2. Saving to safetensors format
|
||||
safetensors_path = "model.safetensors"
|
||||
save_file(clean_state_dict, safetensors_path)
|
||||
print(f"Successfully migrated weights to {safetensors_path}")
|
||||
|
||||
# 3. Loading back safely
|
||||
# No flags needed; it is fundamentally impossible to embed a pickle exploit here
|
||||
safe_loaded_data = load_file(safetensors_path)
|
||||
print("Safetensors file loaded with absolute mathematical safety.")
|
||||
9
lab2/securely_load.py
Normal file
9
lab2/securely_load.py
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
import torch
|
||||
|
||||
print("\nAttempting to load with weights_only=True...")
|
||||
try:
|
||||
loaded_data = torch.load("compromised_model.pt", weights_only=True)
|
||||
print("Model loaded safely.")
|
||||
except Exception as e:
|
||||
print(f"\n[SAFE] PyTorch blocked the exploit! Error: {e}")
|
||||
64
lab4/generate_spam_data.py
Normal file
64
lab4/generate_spam_data.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import csv
|
||||
import random
|
||||
|
||||
random.seed(42)
|
||||
|
||||
SPAM_TEMPLATES = [
|
||||
"CONGRATULATIONS! You've WON a {prize}! Click {link} now to claim!!!",
|
||||
"URGENT: Your account will be suspended. Verify at {link} immediately",
|
||||
"Make ${amount} a week working from home, no experience needed! {link}",
|
||||
"FREE {prize} for the first 100 people! Limited time offer {link}",
|
||||
"Dear winner, you have been selected to receive {amount} dollars. Reply now",
|
||||
"Lowest prices on {product}! Buy now and save 90%! {link}",
|
||||
"Your {product} order could not be delivered, click {link} to reschedule",
|
||||
"Hot singles in your area want to meet you! {link}",
|
||||
"Act now! This {product} offer expires in 24 hours {link}",
|
||||
"You have 1 unread message regarding your {amount} refund, claim at {link}",
|
||||
]
|
||||
|
||||
HAM_TEMPLATES = [
|
||||
"Hey, are we still on for lunch tomorrow at noon?",
|
||||
"Please find attached the {product} report you requested.",
|
||||
"Can you review the pull request I sent over this morning?",
|
||||
"Reminder: team meeting moved to 3pm today.",
|
||||
"Thanks for your help with the {product} demo yesterday.",
|
||||
"The invoice for {amount} has been processed, receipt attached.",
|
||||
"Let's catch up sometime this week, been a while.",
|
||||
"I'll be out of office next Monday, please contact the team lead.",
|
||||
"Great presentation earlier, learned a lot about {product}.",
|
||||
"Attached is the agenda for tomorrow's {product} planning session.",
|
||||
]
|
||||
|
||||
PRIZES = ["iPhone", "vacation package", "gift card", "laptop", "cash prize"]
|
||||
PRODUCTS = ["software", "quarterly", "marketing", "onboarding", "budget"]
|
||||
LINKS = ["http://bit.ly/xyz123", "http://claim-now.co/win", "http://free-gift.biz"]
|
||||
AMOUNTS = ["500", "1000", "2500", "10000"]
|
||||
|
||||
|
||||
def fill(template):
|
||||
return template.format(
|
||||
prize=random.choice(PRIZES),
|
||||
link=random.choice(LINKS),
|
||||
amount=random.choice(AMOUNTS),
|
||||
product=random.choice(PRODUCTS),
|
||||
)
|
||||
|
||||
|
||||
def generate(n_per_class=300):
|
||||
rows = []
|
||||
for _ in range(n_per_class):
|
||||
rows.append((fill(random.choice(SPAM_TEMPLATES)), 1,
|
||||
round(min(1.0, max(0.0, random.gauss(0.85, 0.1))), 3)))
|
||||
rows.append((fill(random.choice(HAM_TEMPLATES)), 0,
|
||||
round(min(1.0, max(0.0, random.gauss(0.08, 0.07))), 3)))
|
||||
random.shuffle(rows)
|
||||
return rows
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
rows = generate(n_per_class=300)
|
||||
with open("data.csv", "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["text", "label", "spam_score"])
|
||||
writer.writerows(rows)
|
||||
print(f"Wrote {len(rows)} rows to data.csv")
|
||||
15
lab4/test_model.py
Normal file
15
lab4/test_model.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import joblib
|
||||
|
||||
vectorizer = joblib.load("vectorizer.joblib")
|
||||
clf = joblib.load("classifier.joblib")
|
||||
|
||||
print("Type text to classify. Ctrl+C or empty line to quit.\n")
|
||||
while True:
|
||||
text = input("> ").strip()
|
||||
if not text:
|
||||
break
|
||||
X = vectorizer.transform([text])
|
||||
pred = clf.predict(X)[0]
|
||||
verdict = "SPAM" if pred == 1 else "HAM"
|
||||
print(f"[{verdict}] {text}\n")
|
||||
|
||||
75
lab4/train_spam_model.py
Normal file
75
lab4/train_spam_model.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import joblib
|
||||
import csv
|
||||
|
||||
import numpy as np
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.linear_model import LogisticRegression, Ridge
|
||||
from sklearn.metrics import (
|
||||
accuracy_score,
|
||||
classification_report,
|
||||
mean_absolute_error,
|
||||
r2_score,
|
||||
)
|
||||
from sklearn.model_selection import train_test_split
|
||||
|
||||
|
||||
def load_data(path="data.csv"):
|
||||
texts, labels, scores = [], [], []
|
||||
with open(path, newline="", encoding="utf-8") as f:
|
||||
for row in csv.DictReader(f):
|
||||
texts.append(row["text"])
|
||||
labels.append(int(row["label"]))
|
||||
scores.append(float(row["spam_score"]))
|
||||
return texts, np.array(labels), np.array(scores)
|
||||
|
||||
|
||||
def main():
|
||||
texts, labels, scores = load_data()
|
||||
|
||||
X_train_txt, X_test_txt, y_train, y_test, s_train, s_test = train_test_split(
|
||||
texts, labels, scores, test_size=0.25, random_state=42, stratify=labels
|
||||
)
|
||||
|
||||
vectorizer = TfidfVectorizer(lowercase=True, ngram_range=(1, 2), max_features=2000)
|
||||
X_train = vectorizer.fit_transform(X_train_txt)
|
||||
X_test = vectorizer.transform(X_test_txt)
|
||||
|
||||
# Classification Model (Spam/Ham)
|
||||
clf = LogisticRegression(max_iter=1000)
|
||||
clf.fit(X_train, y_train)
|
||||
y_pred = clf.predict(X_test)
|
||||
|
||||
print("Classification: Spam vs Ham")
|
||||
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
|
||||
print(classification_report(y_test, y_pred, target_names=["ham", "spam"]))
|
||||
|
||||
# Regression model
|
||||
reg = Ridge(alpha=1.0)
|
||||
reg.fit(X_train, s_train)
|
||||
s_pred = reg.predict(X_test)
|
||||
s_pred = np.clip(s_pred, 0, 1)
|
||||
|
||||
print("Regression: Spam Score (0-1)")
|
||||
print(f"MAE: {mean_absolute_error(s_test, s_pred):.3f}")
|
||||
print(f"R^2: {r2_score(s_test, s_pred):.3f}")
|
||||
|
||||
# Demo model
|
||||
print("\nTest Samples")
|
||||
samples = [
|
||||
"Congratulations! You've won a free vacation, click here now!",
|
||||
"Hey, can you send me the meeting notes from earlier?",
|
||||
]
|
||||
X_demo = vectorizer.transform(samples)
|
||||
preds = clf.predict(X_demo)
|
||||
scores_demo = np.clip(reg.predict(X_demo), 0, 1)
|
||||
for text, label, score in zip(samples, preds, scores_demo):
|
||||
verdict = "SPAM" if label == 1 else "HAM"
|
||||
print(f"[{verdict}] score={score:.2f} :: {text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# persist our model
|
||||
joblib.dump(vectorizer, "vectorizer.joblib")
|
||||
joblib.dump(clf, "classifier.joblib")
|
||||
print("\nSaved model artifacts.")
|
||||
main()
|
||||
56
lab5/generate_http_data.py
Normal file
56
lab5/generate_http_data.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import csv
|
||||
import random
|
||||
|
||||
random.seed(42)
|
||||
|
||||
MALICIOUS_TEMPLATES = [
|
||||
"<script>alert('xss')</script>",
|
||||
"<img src=x onerror=alert(1)>",
|
||||
"<svg onload=alert(1)>",
|
||||
"javascript:alert(document.cookie)",
|
||||
"<iframe src='javascript:alert(1)'></iframe>",
|
||||
"'; system('rm -rf /')",
|
||||
"$(cat /etc/passwd)",
|
||||
"`whoami`",
|
||||
"<body onload=alert('xss')>",
|
||||
"../../../../etc/passwd",
|
||||
"<a href=\"javascript:void(0)\" onclick=\"alert(1)\">click</a>",
|
||||
"%3Cscript%3Ealert(1)%3C/script%3E",
|
||||
"; ping -c 10 127.0.0.1;",
|
||||
"<input onfocus=alert(1) autofocus>",
|
||||
]
|
||||
|
||||
BENIGN_TEMPLATES = [
|
||||
"search term: red shoes size {n}",
|
||||
"username: alex_{n}",
|
||||
"comment: I really liked this product, arrived in {n} days",
|
||||
"city: Springfield",
|
||||
"message: see you at {n}pm tomorrow",
|
||||
"review title: Great value for {n} dollars",
|
||||
"address line 2: Apt {n}B",
|
||||
"notes: please call before {n}pm",
|
||||
"tag: outdoor, hiking, {n}-season",
|
||||
"bio: software engineer, {n} years experience",
|
||||
]
|
||||
|
||||
|
||||
def fill(template):
|
||||
return template.format(n=random.randint(2, 30))
|
||||
|
||||
|
||||
def generate(n_per_class=250):
|
||||
rows = []
|
||||
for _ in range(n_per_class):
|
||||
rows.append((random.choice(MALICIOUS_TEMPLATES), 1))
|
||||
rows.append((fill(random.choice(BENIGN_TEMPLATES)), 0))
|
||||
random.shuffle(rows)
|
||||
return rows
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
rows = generate(n_per_class=250)
|
||||
with open("data.csv", "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["text", "label"])
|
||||
writer.writerows(rows)
|
||||
print(f"Wrote {len(rows)} rows to data.csv")
|
||||
60
lab5/generate_sql_data.py
Normal file
60
lab5/generate_sql_data.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import csv
|
||||
import random
|
||||
|
||||
random.seed(42)
|
||||
|
||||
SQLI_TEMPLATES = [
|
||||
"' OR '1'='1",
|
||||
"' OR 1=1 --",
|
||||
"admin'--",
|
||||
"' UNION SELECT username, password FROM users--",
|
||||
"1; DROP TABLE users;--",
|
||||
"' AND SLEEP(5)--",
|
||||
"\" OR \"\"=\"",
|
||||
"1' AND '1'='1' UNION SELECT NULL,NULL--",
|
||||
"'; EXEC xp_cmdshell('dir')--",
|
||||
"' OR 'a'='a",
|
||||
"1 OR 1=1",
|
||||
"' OR EXISTS(SELECT * FROM users)--",
|
||||
"%27%20OR%20%271%27%3D%271",
|
||||
"'/**/OR/**/1=1--",
|
||||
]
|
||||
|
||||
BENIGN_TEMPLATES = [
|
||||
"john.doe@example.com",
|
||||
"Jane O'Brien",
|
||||
"New York, NY",
|
||||
"Sunset Boulevard Apt 4B",
|
||||
"search term: winter jackets",
|
||||
"user123",
|
||||
"2024-05-19",
|
||||
"5 stars, would buy again",
|
||||
"quantity: 3",
|
||||
"order #{n}",
|
||||
"product review for {n}",
|
||||
"phone: 555-01{n}",
|
||||
"comment: great service, thanks!",
|
||||
"coupon code SAVE{n}",
|
||||
]
|
||||
|
||||
|
||||
def fill(template):
|
||||
return template.format(n=random.randint(10, 99))
|
||||
|
||||
|
||||
def generate(n_per_class=250):
|
||||
rows = []
|
||||
for _ in range(n_per_class):
|
||||
rows.append((random.choice(SQLI_TEMPLATES), 1))
|
||||
rows.append((fill(random.choice(BENIGN_TEMPLATES)), 0))
|
||||
random.shuffle(rows)
|
||||
return rows
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
rows = generate(n_per_class=250)
|
||||
with open("data.csv", "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["text", "label"])
|
||||
writer.writerows(rows)
|
||||
print(f"Wrote {len(rows)} rows to data.csv")
|
||||
15
lab5/test_sqli.py
Normal file
15
lab5/test_sqli.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import joblib
|
||||
|
||||
vectorizer = joblib.load("sqli_vectorizer.joblib")
|
||||
clf = joblib.load("sqli_classifier.joblib")
|
||||
|
||||
print("Type text to classify. Ctrl+C or empty line to quit.\n")
|
||||
while True:
|
||||
text = input("> ").strip()
|
||||
if not text:
|
||||
break
|
||||
X = vectorizer.transform([text])
|
||||
pred = clf.predict(X)[0]
|
||||
verdict = "MALICIOUS" if pred == 1 else "benign"
|
||||
print(f"[{verdict}] {text}\n")
|
||||
|
||||
15
lab5/test_xss.py
Normal file
15
lab5/test_xss.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import joblib
|
||||
|
||||
vectorizer = joblib.load("xss_vectorizer.joblib")
|
||||
clf = joblib.load("xss_classifier.joblib")
|
||||
|
||||
print("Type text to classify. Ctrl+C or empty line to quit.\n")
|
||||
while True:
|
||||
text = input("> ").strip()
|
||||
if not text:
|
||||
break
|
||||
X = vectorizer.transform([text])
|
||||
pred = clf.predict(X)[0]
|
||||
verdict = "MALICIOUS" if pred == 1 else "benign"
|
||||
print(f"[{verdict}] {text}\n")
|
||||
|
||||
77
lab5/train_http_model.py
Normal file
77
lab5/train_http_model.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import joblib
|
||||
import csv
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse import hstack
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.svm import LinearSVC
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from common.features import handcrafted_feature_matrix # noqa: E402
|
||||
|
||||
XSS_KEYWORDS = [
|
||||
"<script", "onerror", "onload", "javascript:", "onclick", "onfocus",
|
||||
"<iframe", "<svg", "alert(", "system(", "etc/passwd", "cmd", "eval(",
|
||||
]
|
||||
|
||||
|
||||
def load_data(path="data.csv"):
|
||||
texts, labels = [], []
|
||||
with open(path, newline="", encoding="utf-8") as f:
|
||||
for row in csv.DictReader(f):
|
||||
texts.append(row["text"])
|
||||
labels.append(int(row["label"]))
|
||||
return texts, np.array(labels)
|
||||
|
||||
|
||||
def main():
|
||||
texts, labels = load_data()
|
||||
X_train_txt, X_test_txt, y_train, y_test = train_test_split(
|
||||
texts, labels, test_size=0.25, random_state=42, stratify=labels
|
||||
)
|
||||
|
||||
vectorizer = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 4), max_features=1500)
|
||||
Xtr_tfidf = vectorizer.fit_transform(X_train_txt)
|
||||
Xte_tfidf = vectorizer.transform(X_test_txt)
|
||||
|
||||
Xtr_hand = handcrafted_feature_matrix(X_train_txt, XSS_KEYWORDS)
|
||||
Xte_hand = handcrafted_feature_matrix(X_test_txt, XSS_KEYWORDS)
|
||||
|
||||
X_train = hstack([Xtr_tfidf, Xtr_hand])
|
||||
X_test = hstack([Xte_tfidf, Xte_hand])
|
||||
|
||||
clf = LinearSVC(random_state=42, max_iter=5000)
|
||||
clf.fit(X_train, y_train)
|
||||
y_pred = clf.predict(X_test)
|
||||
|
||||
print("HTTP / XSS Injection Detection")
|
||||
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
|
||||
print(classification_report(y_test, y_pred, target_names=["benign", "malicious"]))
|
||||
print("Confusion matrix [[TN FP][FN TP]]:")
|
||||
print(confusion_matrix(y_test, y_pred))
|
||||
|
||||
print("\nTest Samples")
|
||||
samples = [
|
||||
"<script>alert(document.cookie)</script>",
|
||||
"search term: comfortable running shoes",
|
||||
"<img src=x onerror=alert(1)>",
|
||||
"notes: please call before 5pm",
|
||||
]
|
||||
Xd_tfidf = vectorizer.transform(samples)
|
||||
Xd_hand = handcrafted_feature_matrix(samples, XSS_KEYWORDS)
|
||||
Xd = hstack([Xd_tfidf, Xd_hand])
|
||||
preds = clf.predict(Xd)
|
||||
for text, label in zip(samples, preds):
|
||||
verdict = "MALICIOUS" if label == 1 else "benign"
|
||||
print(f"[{verdict}] {text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
joblib.dump(vectorizer, "xss_vectorizer.joblib")
|
||||
joblib.dump(clf, "xss_classifier.joblib")
|
||||
print("\nSaved model artifacts. Run: python test_model.py")
|
||||
main()
|
||||
77
lab5/train_sqli_model.py
Normal file
77
lab5/train_sqli_model.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import joblib
|
||||
import csv
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse import hstack
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
|
||||
from sklearn.model_selection import train_test_split
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from common.features import handcrafted_feature_matrix # noqa: E402
|
||||
|
||||
SQL_KEYWORDS = [
|
||||
"select", "union", "drop", "insert", "update", "delete", "or 1=1",
|
||||
"sleep(", "xp_cmdshell", "--", "exec ", "'or'", "1=1",
|
||||
]
|
||||
|
||||
|
||||
def load_data(path="data.csv"):
|
||||
texts, labels = [], []
|
||||
with open(path, newline="", encoding="utf-8") as f:
|
||||
for row in csv.DictReader(f):
|
||||
texts.append(row["text"])
|
||||
labels.append(int(row["label"]))
|
||||
return texts, np.array(labels)
|
||||
|
||||
|
||||
def main():
|
||||
texts, labels = load_data()
|
||||
X_train_txt, X_test_txt, y_train, y_test = train_test_split(
|
||||
texts, labels, test_size=0.25, random_state=42, stratify=labels
|
||||
)
|
||||
|
||||
vectorizer = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 4), max_features=1500)
|
||||
Xtr_tfidf = vectorizer.fit_transform(X_train_txt)
|
||||
Xte_tfidf = vectorizer.transform(X_test_txt)
|
||||
|
||||
Xtr_hand = handcrafted_feature_matrix(X_train_txt, SQL_KEYWORDS)
|
||||
Xte_hand = handcrafted_feature_matrix(X_test_txt, SQL_KEYWORDS)
|
||||
|
||||
X_train = hstack([Xtr_tfidf, Xtr_hand])
|
||||
X_test = hstack([Xte_tfidf, Xte_hand])
|
||||
|
||||
clf = RandomForestClassifier(n_estimators=200, random_state=42)
|
||||
clf.fit(X_train, y_train)
|
||||
y_pred = clf.predict(X_test)
|
||||
|
||||
print("SQL Injection Detection")
|
||||
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
|
||||
print(classification_report(y_test, y_pred, target_names=["benign", "malicious"]))
|
||||
print("Confusion matrix [[TN FP][FN TP]]:")
|
||||
print(confusion_matrix(y_test, y_pred))
|
||||
|
||||
print("\nTest Samples")
|
||||
samples = [
|
||||
"' OR '1'='1' --",
|
||||
"jane.doe@example.com",
|
||||
"1; DROP TABLE orders;--",
|
||||
"search: blue running shoes size 10",
|
||||
]
|
||||
Xd_tfidf = vectorizer.transform(samples)
|
||||
Xd_hand = handcrafted_feature_matrix(samples, SQL_KEYWORDS)
|
||||
Xd = hstack([Xd_tfidf, Xd_hand])
|
||||
preds = clf.predict(Xd)
|
||||
for text, label in zip(samples, preds):
|
||||
verdict = "MALICIOUS" if label == 1 else "benign"
|
||||
print(f"[{verdict}] {text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
joblib.dump(vectorizer, "sqli_vectorizer.joblib")
|
||||
joblib.dump(clf, "sqli_classifier.joblib")
|
||||
print("\nSaved model artifacts. Run: python test_model.py")
|
||||
main()
|
||||
6
main.py
Normal file
6
main.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def main():
|
||||
print("Hello from ai-sec!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
27
pyproject.toml
Normal file
27
pyproject.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[project]
|
||||
name = "ai-sec"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"accelerate>=1.10.0",
|
||||
"huggingface-hub>=0.30.0",
|
||||
"ipywidgets>=8.1.0",
|
||||
"jupyterlab>=4.2.0",
|
||||
"matplotlib>=3.9.0",
|
||||
"numpy>=2.0.0",
|
||||
"safetensors>=0.8.0",
|
||||
"scikit-learn>=1.9.0",
|
||||
"scipy>=1.17.1",
|
||||
"torch>=2.11.0",
|
||||
"transformers>=5.0.0",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
torch = { index = "pytorch-cpu" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "pytorch-cpu"
|
||||
url = "https://download.pytorch.org/whl/cpu"
|
||||
explicit = true
|
||||
20
shell.nix
Normal file
20
shell.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
name = "ai-security-lab";
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
python311
|
||||
uv
|
||||
conda
|
||||
git
|
||||
stdenv.cc.cc.lib
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH"
|
||||
export CONDA_ENVS_PATH="$PWD/.conda/envs"
|
||||
export CONDA_PKGS_DIRS="$PWD/.conda/pkgs"
|
||||
mkdir -p "$CONDA_ENVS_PATH" "$CONDA_PKGS_DIRS"
|
||||
'';
|
||||
}
|
||||
13
test.py
Normal file
13
test.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
import torch
|
||||
import safetensors
|
||||
import transformers
|
||||
|
||||
print(f"torch version: {torch.__version__}")
|
||||
print(f"safetensors version: {safetensors.__version__}")
|
||||
print(f"transformers version: {transformers.__version__}")
|
||||
print(f"CUDA available: {torch.cuda.is_available()}")
|
||||
|
||||
x = torch.tensor([1.0, 2.0, 3.0])
|
||||
y = torch.tensor([4.0, 5.0, 6.0])
|
||||
print(f"x + y = {x + y}")
|
||||
Reference in New Issue
Block a user