Skip to content

Commit

Permalink
added test
Browse files Browse the repository at this point in the history
  • Loading branch information
tsu-ki committed Feb 5, 2025
1 parent 39e76c3 commit b967aaf
Show file tree
Hide file tree
Showing 6 changed files with 470 additions and 969 deletions.
2 changes: 2 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
issue_count,
like_issue,
newhome,
process_bug_image,
remove_user_from_issue,
resolve,
save_issue,
Expand Down Expand Up @@ -861,6 +862,7 @@
path("project/<slug:slug>/", ProjectsDetailView.as_view(), name="projects_detail"),
path("slack/events", slack_events, name="slack_events"),
path("owasp/", TemplateView.as_view(template_name="owasp.html"), name="owasp"),
path("process_bug_image", process_bug_image, name="process_bug_image"),
]

if settings.DEBUG:
Expand Down
1,267 changes: 370 additions & 897 deletions poetry.lock

Large diffs are not rendered by default.

45 changes: 15 additions & 30 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,81 +6,62 @@ authors = ["OWASP BLT <you@example.com>"]
license = "AGPLv3"

[tool.poetry.dependencies]

python = "3.11.2"
python-dotenv = "^1.0.1"
toml = "^0.10.2"
Django = "^5.1.5"
dj-database-url = "^2.3.0"
django-allauth = "^0.61.1"
beautifulsoup4 = "^4.12.3"
colorthief = "^0.2.1"
beautifulsoup4 = "^4.13.3"
django-email-obfuscator = "^0.1.5"
django-gravatar2 = "^1.4.5"
django-import-export = "^4.3.4"
django-import-export = "^4.3.5"
django-annoying = "^0.10.7"
dj-rest-auth = "^5.0.2"
tweepy = "^4.15.0"
Unidecode = "^1.3.8"
user-agents = "^2.2.0"
whitenoise = "^6.8.2"
django-debug-toolbar = "^4.4.6"
selenium = "^4.28.1"

pylibmc = "^1.6.1"
psycopg2-binary = "^2.9.10"
boto = "^2.49.0"
django-cors-headers = "^4.6.0"
protobuf = "^4.25.3"
django-storages = { extras = ["google"], version = "^1.14.3" }
django-timedeltafield = "^0.7.10"
EasyProcess = "^1.1"
"giturlparse.py" = "^0.0.5"
gunicorn = "^23.0.0"
oauthlib = "^3.2.0"
parse = "^1.20.2"
python-openid = "^2.2.5"
pytz = "^2024.1"
requests = "^2.32.3"
requests-oauthlib = "^1.3.1"
six = "^1.16.0"
tablib = "^3.8.0"
ua-parser = "^1.0.0"
ua-parser = "^1.0.1"
djangorestframework = "^3.15.2"
cffi = "^1.17.1"
django-mdeditor = "^0.1.20"
django-tz-detect = "^0.4.0"
django-tellme = "^0.7.3"
django-bootstrap-datepicker-plus = "^5.0.5"
django-star-ratings = "^0.9.2"
stripe = "^8.4.0"
django-environ = "^0.12.0"
django-humanize = "^0.1.2"
drf-yasg = "^1.21.8"
django-simple-captcha = "^0.6.1"
django-filter = "^24.3"
webdriver-manager = "^4.0.2"
pillow = "^10.4.0"
chromedriver-autoinstaller = "^0.6.4"
sentry-sdk = "^2.20.0"
bitcash = "^1.0.2"
pydantic = "^2.10.5"
pydantic = "^2.10.6"
pydantic_core = "^2.18.4"
unstructured = "^0.16.15"
unstructured = "^0.16.17"
Markdown = "^3.6"
faiss-cpu = "^1.8.0"
faiss-cpu = "^1.10.0"
psutil = "^5.9.8"
python-bitcoinrpc = "^1.0"
sendgrid = "^6.11.0"
matplotlib = "^3.10.0"
openpyxl = "^3.1.5"
atproto = "^0.0.55"
slack-bolt = "^1.22.0"
django-redis = "^5.4.0"
uvicorn = "^0.34.0"
channels = "^4.2.0"
channels-redis = "^4.2.1"
async-timeout = "^5.0.1"
aiohttp = "^3.11.11"
drf-yasg = "^1.21.8"
slack-bolt = "^1.22.0"
tld = "0.13"
scout-apm = "^3.3.0"
newrelic = "^10.4.0"
opencv-python = "^4.8.0"
Expand All @@ -89,8 +70,12 @@ numpy = "^1.24.0"
[tool.poetry.group.dev.dependencies]
black = "^24.8.0"
isort = "^5.13.2"
ruff = "^0.9.3"
ruff = "^0.9.4"
pre-commit = "^3.8.0"
selenium = "^4.28.1"
webdriver-manager = "^4.0.2"
chromedriver-autoinstaller = "^0.6.4"
django-debug-toolbar = "^4.4.6"

[tool.isort]
known_first_party = ["blt"]
Expand Down
64 changes: 46 additions & 18 deletions website/templates/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -440,26 +440,54 @@ <h2 class="text-2xl font-semibold leading-7 text-gray-900">{% trans "ADD TEAM ME
event.preventDefault();

}
function clearPreviews() {
manage_div.innerHTML = '';
}

screenshots.addEventListener('change', (event) => {

const fileList = Array.from(event.target.files);

fileList.map(file => {
let src = URL.createObjectURL(file);
let safeName = $("<div>").text(file.name).html();
let safeNameDisplay = safeName.slice(0, 20) + (safeName.length > 20 ? "..." : "");
// Use the safe name for display and in the onclick handler
let fileDiv = $("<div>").addClass("w-full md:w-[300px] h-[180px] overflow-hidden rounded-lg").attr("onclick", `previewFile('${safeName}')`);
let titleDiv = $("<div>").addClass("w-full h-10 flex justify-center rounded-t-lg p-2 bg-gray-500");
let titleP = $("<p>").addClass("text-xl text-white font-bold").text(safeNameDisplay);
let img = $("<img>").addClass("object-cover").attr("src", escapeHtml(src));

titleDiv.append(titleP);
fileDiv.append(titleDiv).append(img);
$("#files_manage").append(fileDiv);
})
screenshots.addEventListener('change', async (event) => {
clearPreviews();

const fileList = Array.from(event.target.files);
for (let file of fileList) {
let formData = new FormData();
formData.append("file", file);
let res = await fetch("/process_bug_image", {
method: "POST",
body: formData
});
if (!res.ok) {
console.log("Error processing file:", file.name);
continue;
}
let data = await res.json();
if (data.error) {
console.log("Error from server:", data.error);
continue;
}
let base64Img = data.image;
let safeName = file.name;
let safeNameDisplay = safeName.slice(0, 20) + (safeName.length > 20 ? "..." : "");

// Build preview element
let fileDiv = document.createElement("div");
fileDiv.className = "w-full md:w-[300px] h-[180px] overflow-hidden rounded-lg";

let titleDiv = document.createElement("div");
titleDiv.className = "w-full h-10 flex justify-center rounded-t-lg p-2 bg-gray-500";

let titleP = document.createElement("p");
titleP.className = "text-xl text-white font-bold";
titleP.textContent = safeNameDisplay;
titleDiv.appendChild(titleP);

let img = document.createElement("img");
img.src = "data:image/jpeg;base64," + base64Img;
img.className = "object-cover";

fileDiv.appendChild(titleDiv);
fileDiv.appendChild(img);
manage_div.appendChild(fileDiv);
}
});


Expand Down
57 changes: 36 additions & 21 deletions website/views/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,9 @@ def delete_issue(request, id):
if request.user.is_superuser or request.user == issue.user or tokenauth:
try:
with transaction.atomic():
Comment.objects.filter(
content_type=ContentType.objects.get_for_model(Issue), object_id=id
).delete()
Comment.objects.filter(content_type=ContentType.objects.get_for_model(Issue), object_id=id).delete()
Points.objects.filter(issue=issue).delete()
Activity.objects.filter(
content_type=ContentType.objects.get_for_model(Issue), object_id=id
).delete()
Activity.objects.filter(content_type=ContentType.objects.get_for_model(Issue), object_id=id).delete()

screenshots = issue.screenshots.all()
for screenshot in screenshots:
Expand All @@ -394,9 +390,7 @@ def delete_issue(request, id):
except Exception as e:
messages.error(request, f"Error deleting issue: {str(e)}")
if tokenauth:
return JsonResponse(
{"status": "error", "message": f"Error deleting issue: {str(e)}"}, status=500
)
return JsonResponse({"status": "error", "message": f"Error deleting issue: {str(e)}"}, status=500)
else:
messages.error(request, "Permission denied")
if tokenauth:
Expand Down Expand Up @@ -1007,23 +1001,19 @@ def create_issue(self, form):
save=True,
)

# Save screenshots
for screenshot in self.request.FILES.getlist("screenshots"):
try:
screenshot.seek(0)

# Read image data
image_data = screenshot.read()
np_img = np.frombuffer(image_data, np.uint8)
img = cv2.imdecode(np_img, cv2.IMREAD_UNCHANGED)

if img is None:
raise ValueError("Failed to decode image")

# Process image - original returned if no faces
img_with_faces_hidden = overlay_faces(img)

# Save processed image
filename = screenshot.name
extension = filename.split(".")[-1].lower()

Expand All @@ -1032,23 +1022,17 @@ def create_issue(self, form):

new_filename = f"{filename[:10]}_{uuid.uuid4()}_{int(time.time())}.{extension}"

# Encode and save image
if extension in ["jpg", "jpeg"]:
_, buffer = cv2.imencode(
".jpg", img_with_faces_hidden, [cv2.IMWRITE_JPEG_QUALITY, 90]
)
_, buffer = cv2.imencode(".jpg", img_with_faces_hidden, [cv2.IMWRITE_JPEG_QUALITY, 90])
else:
_, buffer = cv2.imencode(".png", img_with_faces_hidden)

if buffer is None:
raise ValueError("Failed to encode processed image")

processed_image = ContentFile(buffer.tobytes())
saved_path = default_storage.save(
f"screenshots/{new_filename}", processed_image
)
saved_path = default_storage.save(f"screenshots/{new_filename}", processed_image)

# Create screenshot object
IssueScreenshot.objects.create(image=saved_path, issue=obj)

except Exception as e:
Expand Down Expand Up @@ -1675,3 +1659,34 @@ def flag_issue(request, issue_pk):

def select_bid(request):
return render(request, "bid_selection.html")


@csrf_exempt
def process_bug_image(request):
if request.method == "POST":
file = request.FILES.get("file")
if not file:
return JsonResponse({"error": "No file provided"}, status=400)
try:
import cv2
import numpy as np

from .privacy import overlay_faces

in_memory_file = file.read()
file_array = np.frombuffer(in_memory_file, np.uint8)
img = cv2.imdecode(file_array, cv2.IMREAD_UNCHANGED)
if img is None:
return JsonResponse({"error": "Could not decode image"}, status=400)

processed_img = overlay_faces(img)

ret, buffer = cv2.imencode(".jpg", processed_img, [cv2.IMWRITE_JPEG_QUALITY, 90])
if not ret:
return JsonResponse({"error": "Failed to encode processed image"}, status=500)

base64_data = base64.b64encode(buffer).decode("utf-8")
return JsonResponse({"image": base64_data}, status=200)
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
return JsonResponse({"error": "Only POST allowed"}, status=405)
4 changes: 1 addition & 3 deletions website/views/privacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ def overlay_faces(img, color=(255, 255, 255)):

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
if face_cascade.empty():
return img

Expand Down

0 comments on commit b967aaf

Please sign in to comment.