Merge branch 'Zeecka:main' into main

This commit is contained in:
Aradhya C P
2025-12-30 22:17:13 +05:30
committed by GitHub
4 changed files with 82 additions and 86 deletions
+8 -5
View File
@@ -26,17 +26,20 @@ RUN wget https://github.com/syvaidya/openstego/releases/download/openstego-0.8.6
dpkg -i /tmp/openstego.deb && \ dpkg -i /tmp/openstego.deb && \
rm /tmp/openstego.deb rm /tmp/openstego.deb
# Clean up apt cache and remove useless packages # Copy application
RUN apt-get remove -y wget && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY aperisolve/ /aperisolve/ COPY aperisolve/ /aperisolve/
RUN mkdir -p /data RUN mkdir -p /data
# Install Python dependencies
RUN pip install --no-cache-dir -r /aperisolve/requirements.txt RUN pip install --no-cache-dir -r /aperisolve/requirements.txt
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
# Auto clean after build
RUN apt-get remove -y wget && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "--access-logfile", "-", "--error-logfile", "-", "--log-level", "info", "--capture-output", "aperisolve.wsgi:application"] CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "--access-logfile", "-", "--error-logfile", "-", "--log-level", "info", "--capture-output", "aperisolve.wsgi:application"]
+5 -3
View File
@@ -3,6 +3,7 @@
import shutil import shutil
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import Any
from .utils import MAX_PENDING_TIME, update_data from .utils import MAX_PENDING_TIME, update_data
@@ -17,7 +18,7 @@ def analyze_binwalk(input_img: Path, output_dir: Path) -> None:
stderr = "" stderr = ""
# Run binwalk # Run binwalk
data = subprocess.run( data = subprocess.run(
["binwalk", "-e", "../" + str(image_name), "--run-as=root"], ["binwalk", "--matryoshka", "-e", "../" + str(image_name), "--run-as=root"],
cwd=output_dir, cwd=output_dir,
capture_output=True, capture_output=True,
text=True, text=True,
@@ -47,16 +48,17 @@ def analyze_binwalk(input_img: Path, output_dir: Path) -> None:
# Only report error if stderr exists AND extraction failed. # Only report error if stderr exists AND extraction failed.
# If zip_exist is True, binwalk worked (despite warnings), so we proceed. # If zip_exist is True, binwalk worked (despite warnings), so we proceed.
if len(stderr) > 0 and not zip_exist: if len(stderr) > 0 and not zip_exist:
err = { err: dict[str, dict[str, Any]] = {
"binwalk": { "binwalk": {
"status": "error", "status": "error",
"output": data.stdout.split("\n") if data else [],
"error": stderr, "error": stderr,
} }
} }
update_data(output_dir, err) update_data(output_dir, err)
return None return None
output_data = { output_data: dict[str, dict[str, Any]] = {
"binwalk": { "binwalk": {
"status": "ok", "status": "ok",
"output": data.stdout.split("\n") if data else [], "output": data.stdout.split("\n") if data else [],
+37 -43
View File
@@ -6,6 +6,7 @@ import os
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from shutil import rmtree
from typing import Any, Optional from typing import Any, Optional
from flask import Flask, Response, abort, jsonify, render_template, request, send_file from flask import Flask, Response, abort, jsonify, render_template, request, send_file
@@ -112,34 +113,33 @@ def upload_image() -> tuple[Response, int]:
submission_hash = hashlib.md5(submission_data).hexdigest() submission_hash = hashlib.md5(submission_data).hexdigest()
submission_path = RESULT_FOLDER / img_hash / submission_hash submission_path = RESULT_FOLDER / img_hash / submission_hash
# Check if hash is present on the DB # If submission already exists, return submission hash
submission: Submission = Submission.query.filter_by( if submission_path.exists():
hash=submission_hash submission: Submission = Submission.query.filter_by(
).first() hash=submission_hash
).first()
# Only return if BOTH the file exists AND the DB entry exists if submission is not None: # File exist but not in DB (should not happen),
if submission_path.exists() and submission is not None: rmtree(submission_path) # Removing file and continue processing as new
return jsonify({"submission_hash": submission.hash}), 200 db.session.delete(submission) # pylint: disable=no-member
db.session.commit() # pylint: disable=no-member
else:
return jsonify({"submission_hash": submission.hash}), 200
# If image is new, create a new Image entry
# Self-Healing Logic
new_img_path = RESULT_FOLDER / img_hash / image_name new_img_path = RESULT_FOLDER / img_hash / image_name
sub_img = Image.query.filter_by(hash=img_hash).first() # type: ignore
# Try to find image in DB by hash if new_img_path.parent.exists() and sub_img is None:
sub_img = Image.query.filter_by( # Inconsistent state: image file exists but no DB entry
hash=img_hash rmtree(new_img_path.parent) # Remove the existing files and continue
).first()
# If DB entry is missing (even if file exists on disk), create it! if not new_img_path.parent.exists():
if sub_img is None: new_img_path.parent.mkdir(parents=True, exist_ok=True)
if not new_img_path.parent.exists() or not new_img_path.exists(): with open(new_img_path, "wb") as f: # Write file to disk
new_img_path.parent.mkdir(parents=True, exist_ok=True) f.write(image_data)
with open(new_img_path, "wb") as f:
f.write(image_data)
new_img = Image(
# Recreate the missing Database Entry
sub_img = Image(
file=str(new_img_path), file=str(new_img_path),
hash=img_hash, hash=img_hash,
size=len(image_data), size=len(image_data),
@@ -147,39 +147,33 @@ def upload_image() -> tuple[Response, int]:
first_submission_date=datetime.now(timezone.utc), first_submission_date=datetime.now(timezone.utc),
last_submission_date=datetime.now(timezone.utc), last_submission_date=datetime.now(timezone.utc),
) )
db.session.add(sub_img) db.session.add(new_img) # pylint: disable=no-member
db.session.commit() db.session.commit() # pylint: disable=no-member
# Increment upload count for the image sub_img = Image.query.filter_by(hash=img_hash).first() # type: ignore
sub_img.upload_count += 1 sub_img.upload_count += 1
db.session.commit() db.session.commit() # pylint: disable=no-member
# Create new Submission entry # Create new Submission entry
submission_path.mkdir(parents=True, exist_ok=True) submission_path.mkdir(parents=True, exist_ok=True)
submission = Submission(
filename=image.filename,
password=password,
deep_analysis=deep_analysis,
hash=submission_hash,
status="pending",
date=time.time(),
image_hash=sub_img.hash, # type: ignore
)
db.session.add(submission) # pylint: disable=no-member
# Only create a new submission if it does not already exist db.session.commit() # pylint: disable=no-member
# New submission for new image
if not submission:
submission = Submission(
filename=image.filename,
password=password,
deep_analysis=deep_analysis,
hash=submission_hash,
status="pending",
date=time.time(),
image_hash=sub_img.hash,
)
db.session.add(submission)
db.session.commit()
# Start the analysis jobs # Start the analysis jobs
queue.enqueue("aperisolve.workers.analyze_image", submission.hash, job_timeout=300) queue.enqueue("aperisolve.workers.analyze_image", submission.hash, job_timeout=300)
# Return submission identifier
return jsonify({"submission_hash": submission.hash}), 200 return jsonify({"submission_hash": submission.hash}), 200
@app.route("/status/<hash_val>", methods=["GET"]) @app.route("/status/<hash_val>", methods=["GET"])
def get_status(hash_val: str) -> Response: def get_status(hash_val: str) -> Response:
"""Get the processing status of a submission.""" """Get the processing status of a submission."""
+32 -35
View File
@@ -252,6 +252,38 @@ function parseResult(result) {
analyzer.innerHTML += `<h2>${capitalize(tool.replace("_"," "))}</h2>`; analyzer.innerHTML += `<h2>${capitalize(tool.replace("_"," "))}</h2>`;
// Parse text output
if (typeof result[tool]["output"] === "string") {
analyzer.innerHTML += `<pre>${escapeHtml(
result[tool]["output"]
)}</pre>`;
} else if (Array.isArray(result[tool]["output"])) {
if (result[tool]["output"].length > 0) {
var texarea_content = `<div class="textarea-container">`;
texarea_content += `<textarea class="form-control w-100 mb-2" rows="8" readonly>`;
for (const line of result[tool]["output"]) {
texarea_content += escapeHtml(`${line}\n`);
}
texarea_content += `</textarea>`;
texarea_content += `<i class="fas fa-copy copy-icon"></i>`;
texarea_content += `</div>`;
analyzer.innerHTML += texarea_content;
}
} else if (typeof result[tool]["output"] === "object") {
var table_content = `<div class="table-container">`;
table_content += `<table>`;
for (const key in result[tool]["output"]) {
table_content += `<tr><td>${escapeHtml(key)}</td>`;
table_content += `<td>${escapeHtml(
result[tool]["output"][key]
)}</td></tr>`;
}
table_content += `</table>`;
table_content += `</table>`;
analyzer.innerHTML += table_content;
}
// Parse images, downloads, ...
if (result[tool]["status"] === "ok") { if (result[tool]["status"] === "ok") {
if ("images" in result[tool]) { if ("images" in result[tool]) {
// Parse image output // Parse image output
@@ -286,41 +318,6 @@ function parseResult(result) {
} }
} }
// Parse text output
if (typeof result[tool]["output"] === "string") {
analyzer.innerHTML += `<pre>${escapeHtml(
result[tool]["output"]
)}</pre>`;
} else if (Array.isArray(result[tool]["output"])) {
if (result[tool]["output"].length > 0) {
var texarea_content = `<div class="textarea-container">`;
texarea_content += `<textarea class="form-control w-100 mb-2" rows="8" readonly>`;
for (const line of result[tool]["output"]) {
texarea_content += escapeHtml(`${line}\n`);
}
texarea_content += `</textarea>`;
texarea_content += `<i class="fas fa-copy copy-icon"></i>`;
texarea_content += `</div>`;
analyzer.innerHTML += texarea_content;
}
} else if (typeof result[tool]["output"] === "object") {
var table_content = `<div class="table-container">`;
table_content += `<table>`;
for (const key in result[tool]["output"]) {
table_content += `<tr><td>${escapeHtml(key)}</td>`;
table_content += `<td>${escapeHtml(
result[tool]["output"][key]
)}</td></tr>`;
}
table_content += `</table>`;
table_content += `</table>`;
analyzer.innerHTML += table_content;
} else {
// analyzer.innerHTML += `<pre>${result[tool]["output"]}</pre>`;
}
if ("download" in result[tool]) { if ("download" in result[tool]) {
// Parse download link // Parse download link
analyzer.innerHTML += `<br/><a href="${escapeHtml( analyzer.innerHTML += `<br/><a href="${escapeHtml(