Before / After Code Comparison

6 changed file(s) shown side by side. 8 unchanged or omitted file(s) are not expanded here; use the before and after viewers for full-tree browsing.

docs-worker/app.py

Before remediation

1from __future__ import annotations23import json4import os5import queue6import sqlite37import threading8from datetime import datetime9from pathlib import Path1011from flask import Flask, Response, g, jsonify, request, send_file1213BASE_DIR = Path(__file__).resolve().parent14DATA_DIR = BASE_DIR / "data"15STORAGE_DIR = DATA_DIR / "storage"16DB_PATH = DATA_DIR / "docs.db"1718DATA_DIR.mkdir(parents=True, exist_ok=True)19STORAGE_DIR.mkdir(parents=True, exist_ok=True)2021app = Flask(__name__)22job_queue: "queue.Queue[dict]" = queue.Queue()232425def get_db() -> sqlite3.Connection:26    if "db" not in g:27        conn = sqlite3.connect(DB_PATH)28        conn.row_factory = sqlite3.Row29        g.db = conn30    return g.db313233def init_db() -> None:34    db = sqlite3.connect(DB_PATH)35    db.executescript(36        """37        create table if not exists organizations (38            id integer primary key autoincrement,39            name text not null,40            org_code text not null unique,41            region text not null,42            tier text not null43        );44        create table if not exists teams (45            id integer primary key autoincrement,46            org_id integer not null,47            name text not null,48            cost_center text not null,49            status text not null default 'active'50        );51        create table if not exists users (52            id integer primary key autoincrement,53            org_id integer not null,54            team_id integer,55            email text not null,56            display_name text not null,57            role text not null,58            status text not null default 'active'59        );60        create table if not exists accounts (61            id integer primary key autoincrement,62            org_id integer not null,63            account_no text not null,64            currency text not null,65            balance numeric not null default 066        );67        create table if not exists beneficiaries (68            id integer primary key autoincrement,69            org_id integer not null,70            name text not null,71            bank_name text not null,72            routing text not null73        );74        create table if not exists transfers (75            id integer primary key autoincrement,76            org_id integer not null,77            account_id integer not null,78            beneficiary_id integer not null,79            amount numeric not null,80            currency text not null,81            status text not null,82            memo text not null,83            requested_by text not null84        );85        create table if not exists documents (86            id integer primary key autoincrement,87            org_id integer not null,88            filename text not null,89            original_name text not null,90            mime_type text not null,91            kind text not null,92            created_at text not null default current_timestamp93        );94        create table if not exists risk_reviews (95            id integer primary key autoincrement,96            org_id integer not null,97            subject text not null,98            score numeric not null,99            note text not null,100            created_at text not null default current_timestamp101        );102        create table if not exists audit_records (103            id integer primary key autoincrement,104            org_id integer,105            actor text not null,106            action text not null,107            resource text not null,108            details text not null,109            created_at text not null default current_timestamp110        );111        """112    )113    if db.execute("select count(*) from organizations").fetchone()[0] == 0:114        db.execute(115            "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)",116            ("Acme Treasury", "acme", "us-east", "enterprise"),117        )118        db.execute(119            "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)",120            ("Northwind Holdings", "northwind", "eu-west", "growth"),121        )122        db.commit()123    db.close()124125126def current_identity() -> dict:127    return {128        "org_id": int(request.headers.get("X-Org-Id", "1")),129        "user_id": request.headers.get("X-User-Id", "docs-user"),130        "role": request.headers.get("X-Role", "viewer"),131    }132133134@app.before_request135def load_identity() -> None:136    g.identity = current_identity()137    g.started_at = datetime.utcnow().isoformat()138139140@app.after_request141def audit_request(response: Response) -> Response:142    identity = getattr(g, "identity", None)143    if identity:144        db = get_db()145        db.execute(146            "insert into audit_records (org_id, actor, action, resource, details) values (?, ?, ?, ?, ?)",147            (148                identity["org_id"],149                identity["user_id"],150                f"{request.method} {request.path}",151                request.full_path,152                json.dumps({"status": response.status_code, "args": request.args.to_dict(), "json": request.get_json(silent=True)}),153            ),154        )155        db.commit()156    return response157158159@app.teardown_appcontext160def close_db(exc: Exception | None) -> None:161    db = g.pop("db", None)162    if db is not None:163        db.close()164165166def require_roles(*allowed: str):167    def decorator(fn):168        def wrapped(*args, **kwargs):169            if g.identity["role"] not in allowed:170                return jsonify({"error": "forbidden"}), 403171            return fn(*args, **kwargs)172173        wrapped.__name__ = fn.__name__174        return wrapped175176    return decorator177178179def render_report(org_name: str, title: str, notes: str) -> str:180    return f"""181    <html>182      <body>183        <h1>{title}</h1>184        <h2>{org_name}</h2>185        <div>{notes}</div>186      </body>187    </html>188    """189190191def enqueue_job(payload: dict) -> None:192    job_queue.put(payload)193194195def job_worker() -> None:196    while True:197        payload = job_queue.get()198        if payload is None:199            break200        db = sqlite3.connect(DB_PATH)201        db.execute(202            "insert into audit_records (org_id, actor, action, resource, details) values (?, ?, ?, ?, ?)",203            (204                payload.get("org_id"),205                "system",206                "job.dispatch",207                payload.get("kind", "document"),208                json.dumps(payload),209            ),210        )211        if payload.get("kind") == "document":212            filename = payload["filename"]213            target = STORAGE_DIR / filename214            target.write_text(payload.get("content", ""), encoding="utf-8")215            db.execute(216                "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",217                (218                    payload.get("org_id"),219                    filename,220                    payload.get("original_name", filename),221                    payload.get("mime_type", "text/plain"),222                    payload.get("kind", "document"),223                ),224            )225        db.commit()226        db.close()227        job_queue.task_done()228229230init_db()231threading.Thread(target=job_worker, daemon=True).start()232233234@app.route("/health", methods=["GET"])235def health() -> tuple:236    db = get_db()237    counts = {238        "documents": db.execute("select count(*) from documents").fetchone()[0],239        "audit_records": db.execute("select count(*) from audit_records").fetchone()[0],240        "jobs_pending": job_queue.qsize(),241    }242    return jsonify({"ok": True, "counts": counts})243244245@app.route("/documents", methods=["GET"])246@require_roles("admin", "manager", "viewer")247def search_documents() -> tuple:248    clauses = ["org_id = ?"]249    params: list[object] = [g.identity["org_id"]]250    kind = request.args.get("kind", "").strip()251    name = request.args.get("name", "").strip()252    if kind:253        clauses.append("kind = ?")254        params.append(kind)255    if name:256        clauses.append("original_name like ?")257        params.append(f"%{name}%")258    rows = get_db().execute(259        f"select id, filename, original_name, mime_type, kind, created_at from documents where {' and '.join(clauses)} order by id desc",260        params,261    ).fetchall()262    return jsonify({"data": [dict(row) for row in rows]})263264265@app.route("/docs/render", methods=["POST"])266@require_roles("admin", "manager")267def render_document() -> tuple:268    payload = request.get_json(force=True)269    org_name = payload["orgName"]270    title = payload.get("title", "Treasury Notice")271    notes = payload.get("notes", "")272    filename = payload.get("filename", f"report-{int(datetime.utcnow().timestamp())}.html")273    html = render_report(org_name, title, notes)274    target = STORAGE_DIR / filename275    target.write_text(html, encoding="utf-8")276    db = get_db()277    db.execute(278        "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",279        (g.identity["org_id"], filename, filename, "text/html", "report"),280    )281    db.commit()282    return jsonify({"filename": filename, "path": str(target)}), 201283284285@app.route("/docs/upload", methods=["POST"])286@require_roles("admin", "manager")287def upload_document() -> tuple:288    upload = request.files.get("file")289    if upload is None:290        return jsonify({"error": "missing file"}), 400291    filename = f"{int(datetime.utcnow().timestamp())}-{upload.filename}"292    target = STORAGE_DIR / filename293    upload.save(target)294    db = get_db()295    db.execute(296        "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",297        (g.identity["org_id"], filename, upload.filename, upload.mimetype or "application/octet-stream", "upload"),298    )299    db.commit()300    return jsonify({"filename": filename}), 201301302303@app.route("/docs/download/<path:filename>", methods=["GET"])304@require_roles("admin", "manager", "viewer")305def download_document(filename: str):306    target = STORAGE_DIR / filename307    if not target.exists():308        return jsonify({"error": "not found"}), 404309    return send_file(target, as_attachment=True)310311312@app.route("/jobs/dispatch", methods=["POST"])313@require_roles("admin", "manager")314def dispatch_job() -> tuple:315    payload = request.get_json(force=True)316    payload["org_id"] = g.identity["org_id"]317    enqueue_job(payload)318    return jsonify({"queued": True, "kind": payload.get("kind", "document")}), 202319320321@app.route("/reports/summary", methods=["GET"])322@require_roles("admin", "manager")323def summary_report() -> tuple:324    db = get_db()325    org_id = int(request.args.get("orgId", g.identity["org_id"]))326    title = request.args.get("title", "Document Summary")327    org = db.execute("select name, region, tier from organizations where id = ?", (org_id,)).fetchone()328    if org is None:329        return jsonify({"error": "missing org"}), 404330    notes = request.args.get("notes", "No notes supplied")331    html = render_report(org["name"], title, notes)332    return Response(html, mimetype="text/html")333334335if __name__ == "__main__":336    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", "4300")), debug=False)

After remediation

1from __future__ import annotations23import json4import os5import queue6import sqlite37import threading8from datetime import datetime9from pathlib import Path1011from flask import Flask, Response, g, jsonify, request, send_file1213BASE_DIR = Path(__file__).resolve().parent14DATA_DIR = Path(os.environ.get("DOCS_WORKER_DATA_DIR", str(BASE_DIR / "data"))).resolve()15STORAGE_DIR = DATA_DIR / "storage"16DB_PATH = DATA_DIR / "docs.db"1718DATA_DIR.mkdir(parents=True, exist_ok=True)19STORAGE_DIR.mkdir(parents=True, exist_ok=True)2021app = Flask(__name__)22job_queue: "queue.Queue[dict]" = queue.Queue()232425def get_db() -> sqlite3.Connection:26    if "db" not in g:27        conn = sqlite3.connect(DB_PATH)28        conn.row_factory = sqlite3.Row29        g.db = conn30    return g.db313233def init_db() -> None:34    db = sqlite3.connect(DB_PATH)35    db.executescript(36        """37        create table if not exists organizations (38            id integer primary key autoincrement,39            name text not null,40            org_code text not null unique,41            region text not null,42            tier text not null43        );44        create table if not exists teams (45            id integer primary key autoincrement,46            org_id integer not null,47            name text not null,48            cost_center text not null,49            status text not null default 'active'50        );51        create table if not exists users (52            id integer primary key autoincrement,53            org_id integer not null,54            team_id integer,55            email text not null,56            display_name text not null,57            role text not null,58            status text not null default 'active'59        );60        create table if not exists accounts (61            id integer primary key autoincrement,62            org_id integer not null,63            account_no text not null,64            currency text not null,65            balance numeric not null default 066        );67        create table if not exists beneficiaries (68            id integer primary key autoincrement,69            org_id integer not null,70            name text not null,71            bank_name text not null,72            routing text not null73        );74        create table if not exists transfers (75            id integer primary key autoincrement,76            org_id integer not null,77            account_id integer not null,78            beneficiary_id integer not null,79            amount numeric not null,80            currency text not null,81            status text not null,82            memo text not null,83            requested_by text not null84        );85        create table if not exists documents (86            id integer primary key autoincrement,87            org_id integer not null,88            filename text not null,89            original_name text not null,90            mime_type text not null,91            kind text not null,92            created_at text not null default current_timestamp93        );94        create table if not exists risk_reviews (95            id integer primary key autoincrement,96            org_id integer not null,97            subject text not null,98            score numeric not null,99            note text not null,100            created_at text not null default current_timestamp101        );102        create table if not exists audit_records (103            id integer primary key autoincrement,104            org_id integer,105            actor text not null,106            action text not null,107            resource text not null,108            details text not null,109            created_at text not null default current_timestamp110        );111        """112    )113    if db.execute("select count(*) from organizations").fetchone()[0] == 0:114        db.execute(115            "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)",116            ("Acme Treasury", "acme", "us-east", "enterprise"),117        )118        db.execute(119            "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)",120            ("Northwind Holdings", "northwind", "eu-west", "growth"),121        )122        db.commit()123    db.close()124125126def current_identity() -> dict:127    return {128        "org_id": int(request.headers.get("X-Org-Id", "1")),129        "user_id": request.headers.get("X-User-Id", "docs-user"),130        "role": request.headers.get("X-Role", "viewer"),131    }132133134@app.before_request135def load_identity() -> None:136    g.identity = current_identity()137    g.started_at = datetime.utcnow().isoformat()138139140@app.after_request141def audit_request(response: Response) -> Response:142    identity = getattr(g, "identity", None)143    if identity:144        db = get_db()145        db.execute(146            "insert into audit_records (org_id, actor, action, resource, details) values (?, ?, ?, ?, ?)",147            (148                identity["org_id"],149                identity["user_id"],150                f"{request.method} {request.path}",151                request.full_path,152                json.dumps({"status": response.status_code, "args": request.args.to_dict(), "json": request.get_json(silent=True)}),153            ),154        )155        db.commit()156    return response157158159@app.teardown_appcontext160def close_db(exc: Exception | None) -> None:161    db = g.pop("db", None)162    if db is not None:163        db.close()164165166def require_roles(*allowed: str):167    def decorator(fn):168        def wrapped(*args, **kwargs):169            if g.identity["role"] not in allowed:170                return jsonify({"error": "forbidden"}), 403171            return fn(*args, **kwargs)172173        wrapped.__name__ = fn.__name__174        return wrapped175176    return decorator177178179def render_report(org_name: str, title: str, notes: str) -> str:180    return f"""181    <html>182      <body>183        <h1>{title}</h1>184        <h2>{org_name}</h2>185        <div>{notes}</div>186      </body>187    </html>188    """189190191def resolve_storage_path(filename: str, *, must_exist: bool = False) -> Path:192    if not isinstance(filename, str) or not filename:193        raise ValueError("invalid filename")194    if filename in {".", ".."}:195        raise ValueError("invalid filename")196    if "\x00" in filename:197        raise ValueError("invalid filename")198    candidate_name = Path(filename).name199    if candidate_name != filename or Path(filename).is_absolute():200        raise ValueError("invalid filename")201202    base_dir = STORAGE_DIR.resolve()203    target = base_dir / filename204205    if must_exist:206        resolved = target.resolve(strict=True)207        if resolved.parent != base_dir:208            raise ValueError("invalid filename")209        return resolved210211    if target.exists():212        resolved = target.resolve(strict=True)213        if resolved.parent != base_dir:214            raise ValueError("invalid filename")215216    return target217218219def enqueue_job(payload: dict) -> None:220    job_queue.put(payload)221222223def job_worker() -> None:224    while True:225        payload = job_queue.get()226        if payload is None:227            break228        db = sqlite3.connect(DB_PATH)229        db.execute(230            "insert into audit_records (org_id, actor, action, resource, details) values (?, ?, ?, ?, ?)",231            (232                payload.get("org_id"),233                "system",234                "job.dispatch",235                payload.get("kind", "document"),236                json.dumps(payload),237            ),238        )239        if payload.get("kind") == "document":240            filename = payload["filename"]241            target = STORAGE_DIR / filename242            target.write_text(payload.get("content", ""), encoding="utf-8")243            db.execute(244                "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",245                (246                    payload.get("org_id"),247                    filename,248                    payload.get("original_name", filename),249                    payload.get("mime_type", "text/plain"),250                    payload.get("kind", "document"),251                ),252            )253        db.commit()254        db.close()255        job_queue.task_done()256257258init_db()259threading.Thread(target=job_worker, daemon=True).start()260261262@app.route("/health", methods=["GET"])263def health() -> tuple:264    db = get_db()265    counts = {266        "documents": db.execute("select count(*) from documents").fetchone()[0],267        "audit_records": db.execute("select count(*) from audit_records").fetchone()[0],268        "jobs_pending": job_queue.qsize(),269    }270    return jsonify({"ok": True, "counts": counts})271272273@app.route("/documents", methods=["GET"])274@require_roles("admin", "manager", "viewer")275def search_documents() -> tuple:276    clauses = ["org_id = ?"]277    params: list[object] = [g.identity["org_id"]]278    kind = request.args.get("kind", "").strip()279    name = request.args.get("name", "").strip()280    if kind:281        clauses.append("kind = ?")282        params.append(kind)283    if name:284        clauses.append("original_name like ?")285        params.append(f"%{name}%")286    rows = get_db().execute(287        f"select id, filename, original_name, mime_type, kind, created_at from documents where {' and '.join(clauses)} order by id desc",288        params,289    ).fetchall()290    return jsonify({"data": [dict(row) for row in rows]})291292293@app.route("/docs/render", methods=["POST"])294@require_roles("admin", "manager")295def render_document() -> tuple:296    payload = request.get_json(force=True)297    org_name = payload["orgName"]298    title = payload.get("title", "Treasury Notice")299    notes = payload.get("notes", "")300    filename = payload.get("filename", f"report-{int(datetime.utcnow().timestamp())}.html")301    html = render_report(org_name, title, notes)302    try:303        target = resolve_storage_path(filename)304    except ValueError:305        return jsonify({"error": "invalid filename"}), 400306    target.write_text(html, encoding="utf-8")307    db = get_db()308    db.execute(309        "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",310        (g.identity["org_id"], filename, filename, "text/html", "report"),311    )312    db.commit()313    return jsonify({"filename": filename, "path": str(target)}), 201314315316@app.route("/docs/upload", methods=["POST"])317@require_roles("admin", "manager")318def upload_document() -> tuple:319    upload = request.files.get("file")320    if upload is None:321        return jsonify({"error": "missing file"}), 400322    filename = f"{int(datetime.utcnow().timestamp())}-{upload.filename}"323    try:324        target = resolve_storage_path(filename)325    except ValueError:326        return jsonify({"error": "invalid filename"}), 400327    upload.save(target)328    db = get_db()329    db.execute(330        "insert into documents (org_id, filename, original_name, mime_type, kind) values (?, ?, ?, ?, ?)",331        (g.identity["org_id"], filename, upload.filename, upload.mimetype or "application/octet-stream", "upload"),332    )333    db.commit()334    return jsonify({"filename": filename}), 201335336337@app.route("/docs/download/<path:filename>", methods=["GET"])338@require_roles("admin", "manager", "viewer")339def download_document(filename: str):340    try:341        target = resolve_storage_path(filename, must_exist=True)342    except FileNotFoundError:343        return jsonify({"error": "not found"}), 404344    except ValueError:345        return jsonify({"error": "invalid filename"}), 400346    return send_file(target, as_attachment=True)347348349@app.route("/jobs/dispatch", methods=["POST"])350@require_roles("admin", "manager")351def dispatch_job() -> tuple:352    payload = request.get_json(force=True)353    payload["org_id"] = g.identity["org_id"]354    enqueue_job(payload)355    return jsonify({"queued": True, "kind": payload.get("kind", "document")}), 202356357358@app.route("/reports/summary", methods=["GET"])359@require_roles("admin", "manager")360def summary_report() -> tuple:361    db = get_db()362    org_id = int(request.args.get("orgId", g.identity["org_id"]))363    title = request.args.get("title", "Document Summary")364    org = db.execute("select name, region, tier from organizations where id = ?", (org_id,)).fetchone()365    if org is None:366        return jsonify({"error": "missing org"}), 404367    notes = request.args.get("notes", "No notes supplied")368    html = render_report(org["name"], title, notes)369    return Response(html, mimetype="text/html")370371372if __name__ == "__main__":373    app.run(host="127.0.0.1", port=int(os.environ.get("PORT", "4300")), debug=False)

docs-worker/requirements.txt

Before remediation

1Flask==3.0.3

After remediation

1Flask==3.1.3

docs-worker/test_app.py

Before remediation

missing

After remediation

1import os2import shutil3import tempfile4import importlib5import runpy6import unittest7from pathlib import Path8from urllib.parse import quote9from unittest.mock import patch101112TEST_DATA_DIR = tempfile.mkdtemp(prefix="docs-worker-test-")13os.environ["DOCS_WORKER_DATA_DIR"] = TEST_DATA_DIR1415docs_app = importlib.import_module("app")161718class DownloadDocumentTests(unittest.TestCase):19    @classmethod20    def setUpClass(cls):21        docs_app.app.config["TESTING"] = True22        cls.client = docs_app.app.test_client()23        cls.storage_dir = Path(TEST_DATA_DIR) / "storage"2425    @classmethod26    def tearDownClass(cls):27        shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)2829    def test_download_rejects_traversal_and_symlink_escape(self):30        outside = Path(TEST_DATA_DIR) / "outside.txt"31        outside.write_text("secret", encoding="utf-8")32        self.storage_dir.mkdir(parents=True, exist_ok=True)33        (self.storage_dir / "escape.txt").symlink_to(outside)3435        traversal = self.client.get(36            f"/docs/download/{quote('../outside.txt', safe='')}",37            headers={"X-Role": "viewer", "X-Org-Id": "1"},38        )39        self.assertEqual(traversal.status_code, 400)40        self.assertEqual(traversal.get_json(), {"error": "invalid filename"})4142        symlink = self.client.get(43            "/docs/download/escape.txt",44            headers={"X-Role": "viewer", "X-Org-Id": "1"},45        )46        self.assertEqual(symlink.status_code, 400)47        self.assertEqual(symlink.get_json(), {"error": "invalid filename"})4849    def test_download_returns_existing_file(self):50        self.storage_dir.mkdir(parents=True, exist_ok=True)51        target = self.storage_dir / "statement.txt"52        target.write_text("document-body", encoding="utf-8")5354        response = self.client.get(55            "/docs/download/statement.txt",56            headers={"X-Role": "viewer", "X-Org-Id": "1"},57        )58        self.assertEqual(response.status_code, 200)59        self.assertEqual(response.data, b"document-body")6061    def test_main_binds_to_loopback(self):62        with patch("flask.Flask.run") as run_mock:63            runpy.run_module("app", run_name="__main__")6465        self.assertTrue(run_mock.called)66        _, kwargs = run_mock.call_args67        self.assertEqual(kwargs["host"], "127.0.0.1")68        self.assertEqual(kwargs["debug"], False)697071if __name__ == "__main__":72    unittest.main()

portal/package-lock.json

Before remediation

missing

After remediation

1{2  "name": "treasury-portal",3  "version": "1.0.0",4  "lockfileVersion": 3,5  "requires": true,6  "packages": {7    "": {8      "name": "treasury-portal",9      "version": "1.0.0",10      "dependencies": {11        "better-sqlite3": "^11.10.0",12        "express": "^4.21.2",13        "helmet": "8.3.0",14        "multer": "^1.4.5-lts.1"15      },16      "devDependencies": {17        "@types/express": "^5.0.3",18        "@types/multer": "^1.4.12",19        "@types/node": "^22.15.3",20        "ts-node": "^10.9.2",21        "typescript": "^5.8.3"22      }23    },24    "node_modules/@cspotcode/source-map-support": {25      "version": "0.8.1",26      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",27      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",28      "dev": true,29      "license": "MIT",30      "dependencies": {31        "@jridgewell/trace-mapping": "0.3.9"32      },33      "engines": {34        "node": ">=12"35      }36    },37    "node_modules/@jridgewell/resolve-uri": {38      "version": "3.1.2",39      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",40      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",41      "dev": true,42      "license": "MIT",43      "engines": {44        "node": ">=6.0.0"45      }46    },47    "node_modules/@jridgewell/sourcemap-codec": {48      "version": "1.6.0",49      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",50      "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",51      "dev": true,52      "license": "MIT"53    },54    "node_modules/@jridgewell/trace-mapping": {55      "version": "0.3.9",56      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",57      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",58      "dev": true,59      "license": "MIT",60      "dependencies": {61        "@jridgewell/resolve-uri": "^3.0.3",62        "@jridgewell/sourcemap-codec": "^1.4.10"63      }64    },65    "node_modules/@tsconfig/node10": {66      "version": "1.0.13",67      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz",68      "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==",69      "dev": true,70      "license": "MIT"71    },72    "node_modules/@tsconfig/node12": {73      "version": "1.0.11",74      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",75      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",76      "dev": true,77      "license": "MIT"78    },79    "node_modules/@tsconfig/node14": {80      "version": "1.0.3",81      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",82      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",83      "dev": true,84      "license": "MIT"85    },86    "node_modules/@tsconfig/node16": {87      "version": "1.0.4",88      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",89      "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",90      "dev": true,91      "license": "MIT"92    },93    "node_modules/@types/body-parser": {94      "version": "1.19.6",95      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",96      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",97      "dev": true,98      "license": "MIT",99      "dependencies": {100        "@types/connect": "*",101        "@types/node": "*"102      }103    },104    "node_modules/@types/connect": {105      "version": "3.4.38",106      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",107      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",108      "dev": true,109      "license": "MIT",110      "dependencies": {111        "@types/node": "*"112      }113    },114    "node_modules/@types/express": {115      "version": "5.0.6",116      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",117      "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",118      "dev": true,119      "license": "MIT",120      "dependencies": {121        "@types/body-parser": "*",122        "@types/express-serve-static-core": "^5.0.0",123        "@types/serve-static": "^2"124      }125    },126    "node_modules/@types/express-serve-static-core": {127      "version": "5.1.3",128      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz",129      "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==",130      "dev": true,131      "license": "MIT",132      "dependencies": {133        "@types/node": "*",134        "@types/qs": "*",135        "@types/range-parser": "*",136        "@types/send": "*"137      }138    },139    "node_modules/@types/http-errors": {140      "version": "2.0.5",141      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",142      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",143      "dev": true,144      "license": "MIT"145    },146    "node_modules/@types/multer": {147      "version": "1.4.13",148      "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz",149      "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==",150      "dev": true,151      "license": "MIT",152      "dependencies": {153        "@types/express": "*"154      }155    },156    "node_modules/@types/node": {157      "version": "22.20.2",158      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.2.tgz",159      "integrity": "sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw==",160      "dev": true,161      "license": "MIT",162      "dependencies": {163        "undici-types": "~6.21.0"164      }165    },166    "node_modules/@types/qs": {167      "version": "6.15.1",168      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",169      "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",170      "dev": true,171      "license": "MIT"172    },173    "node_modules/@types/range-parser": {174      "version": "1.2.7",175      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",176      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",177      "dev": true,178      "license": "MIT"179    },180    "node_modules/@types/send": {181      "version": "1.2.1",182      "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",183      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",184      "dev": true,185      "license": "MIT",186      "dependencies": {187        "@types/node": "*"188      }189    },190    "node_modules/@types/serve-static": {191      "version": "2.2.0",192      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",193      "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",194      "dev": true,195      "license": "MIT",196      "dependencies": {197        "@types/http-errors": "*",198        "@types/node": "*"199      }200    },201    "node_modules/accepts": {202      "version": "1.3.8",203      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",204      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",205      "license": "MIT",206      "dependencies": {207        "mime-types": "~2.1.34",208        "negotiator": "0.6.3"209      },210      "engines": {211        "node": ">= 0.6"212      }213    },214    "node_modules/acorn": {215      "version": "8.18.0",216      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",217      "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",218      "dev": true,219      "license": "MIT",220      "bin": {221        "acorn": "bin/acorn"222      },223      "engines": {224        "node": ">=0.4.0"225      }226    },227    "node_modules/acorn-walk": {228      "version": "8.3.5",229      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",230      "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",231      "dev": true,232      "license": "MIT",233      "dependencies": {234        "acorn": "^8.11.0"235      },236      "engines": {237        "node": ">=0.4.0"238      }239    },240    "node_modules/append-field": {241      "version": "1.0.0",242      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",243      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",244      "license": "MIT"245    },246    "node_modules/arg": {247      "version": "4.1.3",248      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",249      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",250      "dev": true,251      "license": "MIT"252    },253    "node_modules/array-flatten": {254      "version": "1.1.1",255      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",256      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",257      "license": "MIT"258    },259    "node_modules/base64-js": {260      "version": "1.5.1",261      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",262      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",263      "funding": [264        {265          "type": "github",266          "url": "https://github.com/sponsors/feross"267        },268        {269          "type": "patreon",270          "url": "https://www.patreon.com/feross"271        },272        {273          "type": "consulting",274          "url": "https://feross.org/support"275        }276      ],277      "license": "MIT"278    },279    "node_modules/better-sqlite3": {280      "version": "11.10.0",281      "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",282      "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",283      "hasInstallScript": true,284      "license": "MIT",285      "dependencies": {286        "bindings": "^1.5.0",287        "prebuild-install": "^7.1.1"288      }289    },290    "node_modules/bindings": {291      "version": "1.5.0",292      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",293      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",294      "license": "MIT",295      "dependencies": {296        "file-uri-to-path": "1.0.0"297      }298    },299    "node_modules/bl": {300      "version": "4.1.0",301      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",302      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",303      "license": "MIT",304      "dependencies": {305        "buffer": "^5.5.0",306        "inherits": "^2.0.4",307        "readable-stream": "^3.4.0"308      }309    },310    "node_modules/bl/node_modules/readable-stream": {311      "version": "3.6.2",312      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",313      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",314      "license": "MIT",315      "dependencies": {316        "inherits": "^2.0.3",317        "string_decoder": "^1.1.1",318        "util-deprecate": "^1.0.1"319      },320      "engines": {321        "node": ">= 6"322      }323    },324    "node_modules/body-parser": {325      "version": "1.20.8",326      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz",327      "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==",328      "license": "MIT",329      "dependencies": {330        "bytes": "~3.1.2",331        "content-type": "~1.0.5",332        "debug": "2.6.9",333        "depd": "2.0.0",334        "destroy": "~1.2.0",335        "http-errors": "~2.0.1",336        "iconv-lite": "~0.4.24",337        "on-finished": "~2.4.1",338        "qs": "~6.16.0",339        "raw-body": "~2.5.3",340        "type-is": "~1.6.18",341        "unpipe": "~1.0.0"342      },343      "engines": {344        "node": ">= 0.8",345        "npm": "1.2.8000 || >= 1.4.16"346      }347    },348    "node_modules/body-parser/node_modules/qs": {349      "version": "6.16.0",350      "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",351      "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",352      "license": "BSD-3-Clause",353      "dependencies": {354        "es-define-property": "^1.0.1",355        "side-channel": "^1.1.1"356      },357      "engines": {358        "node": ">=0.6"359      },360      "funding": {361        "url": "https://github.com/sponsors/ljharb"362      }363    },364    "node_modules/buffer": {365      "version": "5.7.1",366      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",367      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",368      "funding": [369        {370          "type": "github",371          "url": "https://github.com/sponsors/feross"372        },373        {374          "type": "patreon",375          "url": "https://www.patreon.com/feross"376        },377        {378          "type": "consulting",379          "url": "https://feross.org/support"380        }381      ],382      "license": "MIT",383      "dependencies": {384        "base64-js": "^1.3.1",385        "ieee754": "^1.1.13"386      }387    },388    "node_modules/buffer-from": {389      "version": "1.1.2",390      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",391      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",392      "license": "MIT"393    },394    "node_modules/busboy": {395      "version": "1.6.0",396      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",397      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",398      "dependencies": {399        "streamsearch": "^1.1.0"400      },401      "engines": {402        "node": ">=10.16.0"403      }404    },405    "node_modules/bytes": {406      "version": "3.1.2",407      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",408      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",409      "license": "MIT",410      "engines": {411        "node": ">= 0.8"412      }413    },414    "node_modules/call-bind-apply-helpers": {415      "version": "1.0.2",416      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",417      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",418      "license": "MIT",419      "dependencies": {420        "es-errors": "^1.3.0",421        "function-bind": "^1.1.2"422      },423      "engines": {424        "node": ">= 0.4"425      }426    },427    "node_modules/call-bound": {428      "version": "1.0.4",429      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",430      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",431      "license": "MIT",432      "dependencies": {433        "call-bind-apply-helpers": "^1.0.2",434        "get-intrinsic": "^1.3.0"435      },436      "engines": {437        "node": ">= 0.4"438      },439      "funding": {440        "url": "https://github.com/sponsors/ljharb"441      }442    },443    "node_modules/chownr": {444      "version": "1.1.4",445      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",446      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",447      "license": "ISC"448    },449    "node_modules/concat-stream": {450      "version": "1.6.2",451      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",452      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",453      "engines": [454        "node >= 0.8"455      ],456      "license": "MIT",457      "dependencies": {458        "buffer-from": "^1.0.0",459        "inherits": "^2.0.3",460        "readable-stream": "^2.2.2",461        "typedarray": "^0.0.6"462      }463    },464    "node_modules/content-disposition": {465      "version": "0.5.4",466      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",467      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",468      "license": "MIT",469      "dependencies": {470        "safe-buffer": "5.2.1"471      },472      "engines": {473        "node": ">= 0.6"474      }475    },476    "node_modules/content-type": {477      "version": "1.0.5",478      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",479      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",480      "license": "MIT",481      "engines": {482        "node": ">= 0.6"483      }484    },485    "node_modules/cookie": {486      "version": "0.7.2",487      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",488      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",489      "license": "MIT",490      "engines": {491        "node": ">= 0.6"492      }493    },494    "node_modules/cookie-signature": {495      "version": "1.0.7",496      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",497      "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",498      "license": "MIT"499    },500    "node_modules/core-util-is": {501      "version": "1.0.3",502      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",503      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",504      "license": "MIT"505    },506    "node_modules/create-require": {507      "version": "1.1.1",508      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",509      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",510      "dev": true,511      "license": "MIT"512    },513    "node_modules/debug": {514      "version": "2.6.9",515      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",516      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",517      "license": "MIT",518      "dependencies": {519        "ms": "2.0.0"520      }521    },522    "node_modules/decompress-response": {523      "version": "6.0.0",524      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",525      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",526      "license": "MIT",527      "dependencies": {528        "mimic-response": "^3.1.0"529      },530      "engines": {531        "node": ">=10"532      },533      "funding": {534        "url": "https://github.com/sponsors/sindresorhus"535      }536    },537    "node_modules/deep-extend": {538      "version": "0.6.0",539      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",540      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",541      "license": "MIT",542      "engines": {543        "node": ">=4.0.0"544      }545    },546    "node_modules/depd": {547      "version": "2.0.0",548      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",549      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",550      "license": "MIT",551      "engines": {552        "node": ">= 0.8"553      }554    },555    "node_modules/destroy": {556      "version": "1.2.0",557      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",558      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",559      "license": "MIT",560      "engines": {561        "node": ">= 0.8",562        "npm": "1.2.8000 || >= 1.4.16"563      }564    },565    "node_modules/detect-libc": {566      "version": "2.1.2",567      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",568      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",569      "license": "Apache-2.0",570      "engines": {571        "node": ">=8"572      }573    },574    "node_modules/diff": {575      "version": "4.0.4",576      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",577      "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",578      "dev": true,579      "license": "BSD-3-Clause",580      "engines": {581        "node": ">=0.3.1"582      }583    },584    "node_modules/dunder-proto": {585      "version": "1.0.1",586      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",587      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",588      "license": "MIT",589      "dependencies": {590        "call-bind-apply-helpers": "^1.0.1",591        "es-errors": "^1.3.0",592        "gopd": "^1.2.0"593      },594      "engines": {595        "node": ">= 0.4"596      }597    },598    "node_modules/ee-first": {599      "version": "1.1.1",600      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",601      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",602      "license": "MIT"603    },604    "node_modules/encodeurl": {605      "version": "2.0.0",606      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",607      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",608      "license": "MIT",609      "engines": {610        "node": ">= 0.8"611      }612    },613    "node_modules/end-of-stream": {614      "version": "1.4.5",615      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",616      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",617      "license": "MIT",618      "dependencies": {619        "once": "^1.4.0"620      }621    },622    "node_modules/es-define-property": {623      "version": "1.0.1",624      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",625      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",626      "license": "MIT",627      "engines": {628        "node": ">= 0.4"629      }630    },631    "node_modules/es-errors": {632      "version": "1.3.0",633      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",634      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",635      "license": "MIT",636      "engines": {637        "node": ">= 0.4"638      }639    },640    "node_modules/es-object-atoms": {641      "version": "1.1.2",642      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",643      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",644      "license": "MIT",645      "dependencies": {646        "es-errors": "^1.3.0"647      },648      "engines": {649        "node": ">= 0.4"650      }651    },652    "node_modules/escape-html": {653      "version": "1.0.3",654      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",655      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",656      "license": "MIT"657    },658    "node_modules/etag": {659      "version": "1.8.1",660      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",661      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",662      "license": "MIT",663      "engines": {664        "node": ">= 0.6"665      }666    },667    "node_modules/expand-template": {668      "version": "2.0.3",669      "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",670      "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",671      "license": "(MIT OR WTFPL)",672      "engines": {673        "node": ">=6"674      }675    },676    "node_modules/express": {677      "version": "4.22.2",678      "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",679      "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",680      "license": "MIT",681      "dependencies": {682        "accepts": "~1.3.8",683        "array-flatten": "1.1.1",684        "body-parser": "~1.20.5",685        "content-disposition": "~0.5.4",686        "content-type": "~1.0.4",687        "cookie": "~0.7.1",688        "cookie-signature": "~1.0.6",689        "debug": "2.6.9",690        "depd": "2.0.0",691        "encodeurl": "~2.0.0",692        "escape-html": "~1.0.3",693        "etag": "~1.8.1",694        "finalhandler": "~1.3.1",695        "fresh": "~0.5.2",696        "http-errors": "~2.0.0",697        "merge-descriptors": "1.0.3",698        "methods": "~1.1.2",699        "on-finished": "~2.4.1",700        "parseurl": "~1.3.3",701        "path-to-regexp": "~0.1.12",702        "proxy-addr": "~2.0.7",703        "qs": "~6.15.1",704        "range-parser": "~1.2.1",705        "safe-buffer": "5.2.1",706        "send": "~0.19.0",707        "serve-static": "~1.16.2",708        "setprototypeof": "1.2.0",709        "statuses": "~2.0.1",710        "type-is": "~1.6.18",711        "utils-merge": "1.0.1",712        "vary": "~1.1.2"713      },714      "engines": {715        "node": ">= 0.10.0"716      },717      "funding": {718        "type": "opencollective",719        "url": "https://opencollective.com/express"720      }721    },722    "node_modules/file-uri-to-path": {723      "version": "1.0.0",724      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",725      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",726      "license": "MIT"727    },728    "node_modules/finalhandler": {729      "version": "1.3.2",730      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",731      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",732      "license": "MIT",733      "dependencies": {734        "debug": "2.6.9",735        "encodeurl": "~2.0.0",736        "escape-html": "~1.0.3",737        "on-finished": "~2.4.1",738        "parseurl": "~1.3.3",739        "statuses": "~2.0.2",740        "unpipe": "~1.0.0"741      },742      "engines": {743        "node": ">= 0.8"744      }745    },746    "node_modules/forwarded": {747      "version": "0.2.0",748      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",749      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",750      "license": "MIT",751      "engines": {752        "node": ">= 0.6"753      }754    },755    "node_modules/fresh": {756      "version": "0.5.2",757      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",758      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",759      "license": "MIT",760      "engines": {761        "node": ">= 0.6"762      }763    },764    "node_modules/fs-constants": {765      "version": "1.0.0",766      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",767      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",768      "license": "MIT"769    },770    "node_modules/function-bind": {771      "version": "1.1.2",772      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",773      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",774      "license": "MIT",775      "funding": {776        "url": "https://github.com/sponsors/ljharb"777      }778    },779    "node_modules/get-intrinsic": {780      "version": "1.3.0",781      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",782      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",783      "license": "MIT",784      "dependencies": {785        "call-bind-apply-helpers": "^1.0.2",786        "es-define-property": "^1.0.1",787        "es-errors": "^1.3.0",788        "es-object-atoms": "^1.1.1",789        "function-bind": "^1.1.2",790        "get-proto": "^1.0.1",791        "gopd": "^1.2.0",792        "has-symbols": "^1.1.0",793        "hasown": "^2.0.2",794        "math-intrinsics": "^1.1.0"795      },796      "engines": {797        "node": ">= 0.4"798      },799      "funding": {800        "url": "https://github.com/sponsors/ljharb"801      }802    },803    "node_modules/get-proto": {804      "version": "1.0.1",805      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",806      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",807      "license": "MIT",808      "dependencies": {809        "dunder-proto": "^1.0.1",810        "es-object-atoms": "^1.0.0"811      },812      "engines": {813        "node": ">= 0.4"814      }815    },816    "node_modules/github-from-package": {817      "version": "0.0.0",818      "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",819      "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",820      "license": "MIT"821    },822    "node_modules/gopd": {823      "version": "1.2.0",824      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",825      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",826      "license": "MIT",827      "engines": {828        "node": ">= 0.4"829      },830      "funding": {831        "url": "https://github.com/sponsors/ljharb"832      }833    },834    "node_modules/has-symbols": {835      "version": "1.1.0",836      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",837      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",838      "license": "MIT",839      "engines": {840        "node": ">= 0.4"841      },842      "funding": {843        "url": "https://github.com/sponsors/ljharb"844      }845    },846    "node_modules/hasown": {847      "version": "2.0.4",848      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",849      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",850      "license": "MIT",851      "dependencies": {852        "function-bind": "^1.1.2"853      },854      "engines": {855        "node": ">= 0.4"856      }857    },858    "node_modules/helmet": {859      "version": "8.3.0",860      "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.3.0.tgz",861      "integrity": "sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w==",862      "license": "MIT",863      "engines": {864        "node": ">=18.0.0"865      },866      "funding": {867        "url": "https://github.com/sponsors/EvanHahn"868      }869    },870    "node_modules/http-errors": {871      "version": "2.0.1",872      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",873      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",874      "license": "MIT",875      "dependencies": {876        "depd": "~2.0.0",877        "inherits": "~2.0.4",878        "setprototypeof": "~1.2.0",879        "statuses": "~2.0.2",880        "toidentifier": "~1.0.1"881      },882      "engines": {883        "node": ">= 0.8"884      },885      "funding": {886        "type": "opencollective",887        "url": "https://opencollective.com/express"888      }889    },890    "node_modules/iconv-lite": {891      "version": "0.4.24",892      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",893      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",894      "license": "MIT",895      "dependencies": {896        "safer-buffer": ">= 2.1.2 < 3"897      },898      "engines": {899        "node": ">=0.10.0"900      }901    },902    "node_modules/ieee754": {903      "version": "1.2.1",904      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",905      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",906      "funding": [907        {908          "type": "github",909          "url": "https://github.com/sponsors/feross"910        },911        {912          "type": "patreon",913          "url": "https://www.patreon.com/feross"914        },915        {916          "type": "consulting",917          "url": "https://feross.org/support"918        }919      ],920      "license": "BSD-3-Clause"921    },922    "node_modules/inherits": {923      "version": "2.0.4",924      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",925      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",926      "license": "ISC"927    },928    "node_modules/ini": {929      "version": "1.3.8",930      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",931      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",932      "license": "ISC"933    },934    "node_modules/ipaddr.js": {935      "version": "1.9.1",936      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",937      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",938      "license": "MIT",939      "engines": {940        "node": ">= 0.10"941      }942    },943    "node_modules/isarray": {944      "version": "1.0.0",945      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",946      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",947      "license": "MIT"948    },949    "node_modules/make-error": {950      "version": "1.3.6",951      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",952      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",953      "dev": true,954      "license": "ISC"955    },956    "node_modules/math-intrinsics": {957      "version": "1.1.0",958      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",959      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",960      "license": "MIT",961      "engines": {962        "node": ">= 0.4"963      }964    },965    "node_modules/media-typer": {966      "version": "0.3.0",967      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",968      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",969      "license": "MIT",970      "engines": {971        "node": ">= 0.6"972      }973    },974    "node_modules/merge-descriptors": {975      "version": "1.0.3",976      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",977      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",978      "license": "MIT",979      "funding": {980        "url": "https://github.com/sponsors/sindresorhus"981      }982    },983    "node_modules/methods": {984      "version": "1.1.2",985      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",986      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",987      "license": "MIT",988      "engines": {989        "node": ">= 0.6"990      }991    },992    "node_modules/mime": {993      "version": "1.6.0",994      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",995      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",996      "license": "MIT",997      "bin": {998        "mime": "cli.js"999      },1000      "engines": {1001        "node": ">=4"1002      }1003    },1004    "node_modules/mime-db": {1005      "version": "1.52.0",1006      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",1007      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",1008      "license": "MIT",1009      "engines": {1010        "node": ">= 0.6"1011      }1012    },1013    "node_modules/mime-types": {1014      "version": "2.1.35",1015      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",1016      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",1017      "license": "MIT",1018      "dependencies": {1019        "mime-db": "1.52.0"1020      },1021      "engines": {1022        "node": ">= 0.6"1023      }1024    },1025    "node_modules/mimic-response": {1026      "version": "3.1.0",1027      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",1028      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",1029      "license": "MIT",1030      "engines": {1031        "node": ">=10"1032      },1033      "funding": {1034        "url": "https://github.com/sponsors/sindresorhus"1035      }1036    },1037    "node_modules/minimist": {1038      "version": "1.2.8",1039      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",1040      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",1041      "license": "MIT",1042      "funding": {1043        "url": "https://github.com/sponsors/ljharb"1044      }1045    },1046    "node_modules/mkdirp": {1047      "version": "0.5.6",1048      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",1049      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",1050      "license": "MIT",1051      "dependencies": {1052        "minimist": "^1.2.6"1053      },1054      "bin": {1055        "mkdirp": "bin/cmd.js"1056      }1057    },1058    "node_modules/mkdirp-classic": {1059      "version": "0.5.3",1060      "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",1061      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",1062      "license": "MIT"1063    },1064    "node_modules/ms": {1065      "version": "2.0.0",1066      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",1067      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",1068      "license": "MIT"1069    },1070    "node_modules/multer": {1071      "version": "1.4.5-lts.2",1072      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",1073      "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",1074      "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.",1075      "license": "MIT",1076      "dependencies": {1077        "append-field": "^1.0.0",1078        "busboy": "^1.0.0",1079        "concat-stream": "^1.5.2",1080        "mkdirp": "^0.5.4",1081        "object-assign": "^4.1.1",1082        "type-is": "^1.6.4",1083        "xtend": "^4.0.0"1084      },1085      "engines": {1086        "node": ">= 6.0.0"1087      }1088    },1089    "node_modules/napi-build-utils": {1090      "version": "2.0.0",1091      "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",1092      "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",1093      "license": "MIT"1094    },1095    "node_modules/negotiator": {1096      "version": "0.6.3",1097      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",1098      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",1099      "license": "MIT",1100      "engines": {1101        "node": ">= 0.6"1102      }1103    },1104    "node_modules/node-abi": {1105      "version": "3.96.0",1106      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.96.0.tgz",1107      "integrity": "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==",1108      "license": "MIT",1109      "dependencies": {1110        "semver": "^7.3.5"1111      },1112      "engines": {1113        "node": ">=10"1114      }1115    },1116    "node_modules/object-assign": {1117      "version": "4.1.1",1118      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",1119      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",1120      "license": "MIT",1121      "engines": {1122        "node": ">=0.10.0"1123      }1124    },1125    "node_modules/object-inspect": {1126      "version": "1.13.4",1127      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",1128      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",1129      "license": "MIT",1130      "engines": {1131        "node": ">= 0.4"1132      },1133      "funding": {1134        "url": "https://github.com/sponsors/ljharb"1135      }1136    },1137    "node_modules/on-finished": {1138      "version": "2.4.1",1139      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",1140      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",1141      "license": "MIT",1142      "dependencies": {1143        "ee-first": "1.1.1"1144      },1145      "engines": {1146        "node": ">= 0.8"1147      }1148    },1149    "node_modules/once": {1150      "version": "1.4.0",1151      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",1152      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",1153      "license": "ISC",1154      "dependencies": {1155        "wrappy": "1"1156      }1157    },1158    "node_modules/parseurl": {1159      "version": "1.3.3",1160      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",1161      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",1162      "license": "MIT",1163      "engines": {1164        "node": ">= 0.8"1165      }1166    },1167    "node_modules/path-to-regexp": {1168      "version": "0.1.13",1169      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",1170      "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",1171      "license": "MIT"1172    },1173    "node_modules/prebuild-install": {1174      "version": "7.1.3",1175      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",1176      "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",1177      "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",1178      "license": "MIT",1179      "dependencies": {1180        "detect-libc": "^2.0.0",1181        "expand-template": "^2.0.3",1182        "github-from-package": "0.0.0",1183        "minimist": "^1.2.3",1184        "mkdirp-classic": "^0.5.3",1185        "napi-build-utils": "^2.0.0",1186        "node-abi": "^3.3.0",1187        "pump": "^3.0.0",1188        "rc": "^1.2.7",1189        "simple-get": "^4.0.0",1190        "tar-fs": "^2.0.0",1191        "tunnel-agent": "^0.6.0"1192      },1193      "bin": {1194        "prebuild-install": "bin.js"1195      },1196      "engines": {1197        "node": ">=10"1198      }1199    },1200    "node_modules/process-nextick-args": {1201      "version": "2.0.1",1202      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",1203      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",1204      "license": "MIT"1205    },1206    "node_modules/proxy-addr": {1207      "version": "2.0.7",1208      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",1209      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",1210      "license": "MIT",1211      "dependencies": {1212        "forwarded": "0.2.0",1213        "ipaddr.js": "1.9.1"1214      },1215      "engines": {1216        "node": ">= 0.10"1217      }1218    },1219    "node_modules/pump": {1220      "version": "3.0.4",1221      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",1222      "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",1223      "license": "MIT",1224      "dependencies": {1225        "end-of-stream": "^1.1.0",1226        "once": "^1.3.1"1227      }1228    },1229    "node_modules/qs": {1230      "version": "6.15.3",1231      "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",1232      "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",1233      "license": "BSD-3-Clause",1234      "dependencies": {1235        "es-define-property": "^1.0.1",1236        "side-channel": "^1.1.1"1237      },1238      "engines": {1239        "node": ">=0.6"1240      },1241      "funding": {1242        "url": "https://github.com/sponsors/ljharb"1243      }1244    },1245    "node_modules/range-parser": {1246      "version": "1.2.1",1247      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",1248      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",1249      "license": "MIT",1250      "engines": {1251        "node": ">= 0.6"1252      }1253    },1254    "node_modules/raw-body": {1255      "version": "2.5.3",1256      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",1257      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",1258      "license": "MIT",1259      "dependencies": {1260        "bytes": "~3.1.2",1261        "http-errors": "~2.0.1",1262        "iconv-lite": "~0.4.24",1263        "unpipe": "~1.0.0"1264      },1265      "engines": {1266        "node": ">= 0.8"1267      }1268    },1269    "node_modules/rc": {1270      "version": "1.2.8",1271      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",1272      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",1273      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",1274      "dependencies": {1275        "deep-extend": "^0.6.0",1276        "ini": "~1.3.0",1277        "minimist": "^1.2.0",1278        "strip-json-comments": "~2.0.1"1279      },1280      "bin": {1281        "rc": "cli.js"1282      }1283    },1284    "node_modules/readable-stream": {1285      "version": "2.3.8",1286      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",1287      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",1288      "license": "MIT",1289      "dependencies": {1290        "core-util-is": "~1.0.0",1291        "inherits": "~2.0.3",1292        "isarray": "~1.0.0",1293        "process-nextick-args": "~2.0.0",1294        "safe-buffer": "~5.1.1",1295        "string_decoder": "~1.1.1",1296        "util-deprecate": "~1.0.1"1297      }1298    },1299    "node_modules/readable-stream/node_modules/safe-buffer": {1300      "version": "5.1.2",1301      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",1302      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",1303      "license": "MIT"1304    },1305    "node_modules/safe-buffer": {1306      "version": "5.2.1",1307      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",1308      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",1309      "funding": [1310        {1311          "type": "github",1312          "url": "https://github.com/sponsors/feross"1313        },1314        {1315          "type": "patreon",1316          "url": "https://www.patreon.com/feross"1317        },1318        {1319          "type": "consulting",1320          "url": "https://feross.org/support"1321        }1322      ],1323      "license": "MIT"1324    },1325    "node_modules/safer-buffer": {1326      "version": "2.1.2",1327      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",1328      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",1329      "license": "MIT"1330    },1331    "node_modules/semver": {1332      "version": "7.8.5",1333      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",1334      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",1335      "license": "ISC",1336      "bin": {1337        "semver": "bin/semver.js"1338      },1339      "engines": {1340        "node": ">=10"1341      }1342    },1343    "node_modules/send": {1344      "version": "0.19.2",1345      "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",1346      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",1347      "license": "MIT",1348      "dependencies": {1349        "debug": "2.6.9",1350        "depd": "2.0.0",1351        "destroy": "1.2.0",1352        "encodeurl": "~2.0.0",1353        "escape-html": "~1.0.3",1354        "etag": "~1.8.1",1355        "fresh": "~0.5.2",1356        "http-errors": "~2.0.1",1357        "mime": "1.6.0",1358        "ms": "2.1.3",1359        "on-finished": "~2.4.1",1360        "range-parser": "~1.2.1",1361        "statuses": "~2.0.2"1362      },1363      "engines": {1364        "node": ">= 0.8.0"1365      }1366    },1367    "node_modules/send/node_modules/ms": {1368      "version": "2.1.3",1369      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",1370      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",1371      "license": "MIT"1372    },1373    "node_modules/serve-static": {1374      "version": "1.16.3",1375      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",1376      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",1377      "license": "MIT",1378      "dependencies": {1379        "encodeurl": "~2.0.0",1380        "escape-html": "~1.0.3",1381        "parseurl": "~1.3.3",1382        "send": "~0.19.1"1383      },1384      "engines": {1385        "node": ">= 0.8.0"1386      }1387    },1388    "node_modules/setprototypeof": {1389      "version": "1.2.0",1390      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",1391      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",1392      "license": "ISC"1393    },1394    "node_modules/side-channel": {1395      "version": "1.1.1",1396      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",1397      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",1398      "license": "MIT",1399      "dependencies": {1400        "es-errors": "^1.3.0",1401        "object-inspect": "^1.13.4",1402        "side-channel-list": "^1.0.1",1403        "side-channel-map": "^1.0.1",1404        "side-channel-weakmap": "^1.0.2"1405      },1406      "engines": {1407        "node": ">= 0.4"1408      },1409      "funding": {1410        "url": "https://github.com/sponsors/ljharb"1411      }1412    },1413    "node_modules/side-channel-list": {1414      "version": "1.0.1",1415      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",1416      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",1417      "license": "MIT",1418      "dependencies": {1419        "es-errors": "^1.3.0",1420        "object-inspect": "^1.13.4"1421      },1422      "engines": {1423        "node": ">= 0.4"1424      },1425      "funding": {1426        "url": "https://github.com/sponsors/ljharb"1427      }1428    },1429    "node_modules/side-channel-map": {1430      "version": "1.0.1",1431      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",1432      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",1433      "license": "MIT",1434      "dependencies": {1435        "call-bound": "^1.0.2",1436        "es-errors": "^1.3.0",1437        "get-intrinsic": "^1.2.5",1438        "object-inspect": "^1.13.3"1439      },1440      "engines": {1441        "node": ">= 0.4"1442      },1443      "funding": {1444        "url": "https://github.com/sponsors/ljharb"1445      }1446    },1447    "node_modules/side-channel-weakmap": {1448      "version": "1.0.2",1449      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",1450      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",1451      "license": "MIT",1452      "dependencies": {1453        "call-bound": "^1.0.2",1454        "es-errors": "^1.3.0",1455        "get-intrinsic": "^1.2.5",1456        "object-inspect": "^1.13.3",1457        "side-channel-map": "^1.0.1"1458      },1459      "engines": {1460        "node": ">= 0.4"1461      },1462      "funding": {1463        "url": "https://github.com/sponsors/ljharb"1464      }1465    },1466    "node_modules/simple-concat": {1467      "version": "1.0.1",1468      "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",1469      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",1470      "funding": [1471        {1472          "type": "github",1473          "url": "https://github.com/sponsors/feross"1474        },1475        {1476          "type": "patreon",1477          "url": "https://www.patreon.com/feross"1478        },1479        {1480          "type": "consulting",1481          "url": "https://feross.org/support"1482        }1483      ],1484      "license": "MIT"1485    },1486    "node_modules/simple-get": {1487      "version": "4.0.1",1488      "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",1489      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",1490      "funding": [1491        {1492          "type": "github",1493          "url": "https://github.com/sponsors/feross"1494        },1495        {1496          "type": "patreon",1497          "url": "https://www.patreon.com/feross"1498        },1499        {1500          "type": "consulting",1501          "url": "https://feross.org/support"1502        }1503      ],1504      "license": "MIT",1505      "dependencies": {1506        "decompress-response": "^6.0.0",1507        "once": "^1.3.1",1508        "simple-concat": "^1.0.0"1509      }1510    },1511    "node_modules/statuses": {1512      "version": "2.0.2",1513      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",1514      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",1515      "license": "MIT",1516      "engines": {1517        "node": ">= 0.8"1518      }1519    },1520    "node_modules/streamsearch": {1521      "version": "1.1.0",1522      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",1523      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",1524      "engines": {1525        "node": ">=10.0.0"1526      }1527    },1528    "node_modules/string_decoder": {1529      "version": "1.1.1",1530      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",1531      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",1532      "license": "MIT",1533      "dependencies": {1534        "safe-buffer": "~5.1.0"1535      }1536    },1537    "node_modules/string_decoder/node_modules/safe-buffer": {1538      "version": "5.1.2",1539      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",1540      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",1541      "license": "MIT"1542    },1543    "node_modules/strip-json-comments": {1544      "version": "2.0.1",1545      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",1546      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",1547      "license": "MIT",1548      "engines": {1549        "node": ">=0.10.0"1550      }1551    },1552    "node_modules/tar-fs": {1553      "version": "2.1.5",1554      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",1555      "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",1556      "license": "MIT",1557      "dependencies": {1558        "chownr": "^1.1.1",1559        "mkdirp-classic": "^0.5.2",1560        "pump": "^3.0.0",1561        "tar-stream": "^2.1.4"1562      }1563    },1564    "node_modules/tar-stream": {1565      "version": "2.2.0",1566      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",1567      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",1568      "license": "MIT",1569      "dependencies": {1570        "bl": "^4.0.3",1571        "end-of-stream": "^1.4.1",1572        "fs-constants": "^1.0.0",1573        "inherits": "^2.0.3",1574        "readable-stream": "^3.1.1"1575      },1576      "engines": {1577        "node": ">=6"1578      }1579    },1580    "node_modules/tar-stream/node_modules/readable-stream": {1581      "version": "3.6.2",1582      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",1583      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",1584      "license": "MIT",1585      "dependencies": {1586        "inherits": "^2.0.3",1587        "string_decoder": "^1.1.1",1588        "util-deprecate": "^1.0.1"1589      },1590      "engines": {1591        "node": ">= 6"1592      }1593    },1594    "node_modules/toidentifier": {1595      "version": "1.0.1",1596      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",1597      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",1598      "license": "MIT",1599      "engines": {1600        "node": ">=0.6"1601      }1602    },1603    "node_modules/ts-node": {1604      "version": "10.9.2",1605      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",1606      "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",1607      "dev": true,1608      "license": "MIT",1609      "dependencies": {1610        "@cspotcode/source-map-support": "^0.8.0",1611        "@tsconfig/node10": "^1.0.7",1612        "@tsconfig/node12": "^1.0.7",1613        "@tsconfig/node14": "^1.0.0",1614        "@tsconfig/node16": "^1.0.2",1615        "acorn": "^8.4.1",1616        "acorn-walk": "^8.1.1",1617        "arg": "^4.1.0",1618        "create-require": "^1.1.0",1619        "diff": "^4.0.1",1620        "make-error": "^1.1.1",1621        "v8-compile-cache-lib": "^3.0.1",1622        "yn": "3.1.1"1623      },1624      "bin": {1625        "ts-node": "dist/bin.js",1626        "ts-node-cwd": "dist/bin-cwd.js",1627        "ts-node-esm": "dist/bin-esm.js",1628        "ts-node-script": "dist/bin-script.js",1629        "ts-node-transpile-only": "dist/bin-transpile.js",1630        "ts-script": "dist/bin-script-deprecated.js"1631      },1632      "peerDependencies": {1633        "@swc/core": ">=1.2.50",1634        "@swc/wasm": ">=1.2.50",1635        "@types/node": "*",1636        "typescript": ">=2.7"1637      },1638      "peerDependenciesMeta": {1639        "@swc/core": {1640          "optional": true1641        },1642        "@swc/wasm": {1643          "optional": true1644        }1645      }1646    },1647    "node_modules/tunnel-agent": {1648      "version": "0.6.0",1649      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",1650      "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",1651      "license": "Apache-2.0",1652      "dependencies": {1653        "safe-buffer": "^5.0.1"1654      },1655      "engines": {1656        "node": "*"1657      }1658    },1659    "node_modules/type-is": {1660      "version": "1.6.18",1661      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",1662      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",1663      "license": "MIT",1664      "dependencies": {1665        "media-typer": "0.3.0",1666        "mime-types": "~2.1.24"1667      },1668      "engines": {1669        "node": ">= 0.6"1670      }1671    },1672    "node_modules/typedarray": {1673      "version": "0.0.6",1674      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",1675      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",1676      "license": "MIT"1677    },1678    "node_modules/typescript": {1679      "version": "5.9.3",1680      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",1681      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",1682      "dev": true,1683      "license": "Apache-2.0",1684      "bin": {1685        "tsc": "bin/tsc",1686        "tsserver": "bin/tsserver"1687      },1688      "engines": {1689        "node": ">=14.17"1690      }1691    },1692    "node_modules/undici-types": {1693      "version": "6.21.0",1694      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",1695      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",1696      "dev": true,1697      "license": "MIT"1698    },1699    "node_modules/unpipe": {1700      "version": "1.0.0",1701      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",1702      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",1703      "license": "MIT",1704      "engines": {1705        "node": ">= 0.8"1706      }1707    },1708    "node_modules/util-deprecate": {1709      "version": "1.0.2",1710      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",1711      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",1712      "license": "MIT"1713    },1714    "node_modules/utils-merge": {1715      "version": "1.0.1",1716      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",1717      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",1718      "license": "MIT",1719      "engines": {1720        "node": ">= 0.4.0"1721      }1722    },1723    "node_modules/v8-compile-cache-lib": {1724      "version": "3.0.1",1725      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",1726      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",1727      "dev": true,1728      "license": "MIT"1729    },1730    "node_modules/vary": {1731      "version": "1.1.2",1732      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",1733      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",1734      "license": "MIT",1735      "engines": {1736        "node": ">= 0.8"1737      }1738    },1739    "node_modules/wrappy": {1740      "version": "1.0.2",1741      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",1742      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",1743      "license": "ISC"1744    },1745    "node_modules/xtend": {1746      "version": "4.0.2",1747      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",1748      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",1749      "license": "MIT",1750      "engines": {1751        "node": ">=0.4"1752      }1753    },1754    "node_modules/yn": {1755      "version": "3.1.1",1756      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",1757      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",1758      "dev": true,1759      "license": "MIT",1760      "engines": {1761        "node": ">=6"1762      }1763    }1764  }1765}

portal/package.json

Before remediation

1{2  "name": "treasury-portal",3  "private": true,4  "version": "1.0.0",5  "type": "commonjs",6  "scripts": {7    "dev": "ts-node src/server.ts",8    "build": "tsc -p tsconfig.json",9    "start": "node dist/server.js"10  },11  "dependencies": {12    "better-sqlite3": "^11.10.0",13    "express": "^4.21.2",14    "multer": "^1.4.5-lts.1"15  },16  "devDependencies": {17    "@types/express": "^5.0.3",18    "@types/multer": "^1.4.12",19    "@types/node": "^22.15.3",20    "ts-node": "^10.9.2",21    "typescript": "^5.8.3"22  }23}

After remediation

1{2  "name": "treasury-portal",3  "private": true,4  "version": "1.0.0",5  "type": "commonjs",6  "scripts": {7    "dev": "ts-node src/server.ts",8    "build": "tsc -p tsconfig.json",9    "start": "node dist/server.js"10  },11  "dependencies": {12    "better-sqlite3": "^11.10.0",13    "helmet": "8.3.0",14    "express": "^4.21.2",15    "multer": "^1.4.5-lts.1"16  },17  "devDependencies": {18    "@types/express": "^5.0.3",19    "@types/multer": "^1.4.12",20    "@types/node": "^22.15.3",21    "ts-node": "^10.9.2",22    "typescript": "^5.8.3"23  }24}

portal/src/server.ts

Before remediation

1import express, { NextFunction, Request, Response } from "express";2import fs from "fs";3import path from "path";4import multer from "multer";5import { db, recordAudit, storagePath } from "./store.js";67type Identity = {8  orgId: number;9  userId: string;10  role: "admin" | "manager" | "viewer";11};1213declare global {14  namespace Express {15    interface Request {16      identity?: Identity;17    }18  }19}2021const app = express();22const upload = multer({ dest: path.resolve(__dirname, "..", "tmp") });2324fs.mkdirSync(path.resolve(__dirname, "..", "tmp"), { recursive: true });2526app.use(express.json());27app.use(express.urlencoded({ extended: true }));2829app.use((req, res, next) => {30  const orgId = Number(req.header("x-org-id") || "1");31  const userId = req.header("x-user-id") || "portal-user";32  const role = (req.header("x-role") || "viewer") as Identity["role"];33  req.identity = { orgId, userId, role };34  next();35});3637app.use((req, res, next) => {38  const finish = () => {39    if (!req.identity) {40      return;41    }42    recordAudit({43      orgId: req.identity.orgId,44      actor: req.identity.userId,45      action: `${req.method} ${req.path}`,46      resource: req.originalUrl,47      details: {48        statusCode: res.statusCode,49        query: req.query,50        body: req.body51      }52    });53  };54  res.on("finish", finish);55  next();56});5758function requireRole(allowed: Identity["role"][]) {59  return (req: Request, res: Response, next: NextFunction) => {60    if (!req.identity || !allowed.includes(req.identity.role)) {61      res.status(403).json({ error: "forbidden" });62      return;63    }64    next();65  };66}6768function buildOrgFilters(query: Request["query"]) {69  const clauses: string[] = [];70  const params: unknown[] = [];7172  if (typeof query.name === "string" && query.name.trim()) {73    clauses.push("name like ?");74    params.push(`%${query.name.trim()}%`);75  }76  if (typeof query.region === "string" && query.region.trim()) {77    clauses.push("region = ?");78    params.push(query.region.trim());79  }80  if (typeof query.tier === "string" && query.tier.trim()) {81    clauses.push("tier = ?");82    params.push(query.tier.trim());83  }8485  const where = clauses.length ? ` where ${clauses.join(" and ")}` : "";86  return { where, params };87}8889app.get("/health", (_req, res) => {90  const counts = {91    organizations: db.prepare("select count(*) as count from organizations").get().count,92    transfers: db.prepare("select count(*) as count from transfers").get().count,93    auditRecords: db.prepare("select count(*) as count from audit_records").get().count94  };95  res.json({ ok: true, counts });96});9798app.get("/orgs", requireRole(["admin", "manager", "viewer"]), (req, res) => {99  const { where, params } = buildOrgFilters(req.query);100  const rows = db.prepare(`select * from organizations${where} order by id desc`).all(...params);101  res.json({ data: rows });102});103104app.post("/orgs", requireRole(["admin"]), (req, res) => {105  const { name, orgCode, region, tier } = req.body;106  const stmt = db.prepare(107    "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)"108  );109  const result = stmt.run(name, orgCode, region, tier);110  res.status(201).json({ id: result.lastInsertRowid, name, orgCode, region, tier });111});112113app.get("/teams", requireRole(["admin", "manager"]), (req, res) => {114  const filters: string[] = [];115  const params: unknown[] = [];116117  if (typeof req.query.orgId === "string" && req.query.orgId.trim()) {118    filters.push("org_id = ?");119    params.push(Number(req.query.orgId));120  }121  if (typeof req.query.query === "string" && req.query.query.trim()) {122    filters.push("(name like ? or cost_center like ?)");123    params.push(`%${req.query.query.trim()}%`, `%${req.query.query.trim()}%`);124  }125126  const where = filters.length ? ` where ${filters.join(" and ")}` : "";127  const rows = db.prepare(`select * from teams${where} order by created_at desc`).all(...params);128  res.json({ data: rows });129});130131app.post("/users", requireRole(["admin", "manager"]), (req, res) => {132  const { orgId, teamId, email, displayName, role, status } = req.body;133  const result = db134    .prepare(135      "insert into users (org_id, team_id, email, display_name, role, status) values (?, ?, ?, ?, ?, ?)"136    )137    .run(orgId, teamId ?? null, email, displayName, role, status || "active");138  res.status(201).json({ id: result.lastInsertRowid, orgId, teamId, email, displayName, role });139});140141app.post("/beneficiaries", requireRole(["admin", "manager"]), (req, res) => {142  const { orgId, name, bankName, routing } = req.body;143  const result = db144    .prepare("insert into beneficiaries (org_id, name, bank_name, routing) values (?, ?, ?, ?)")145    .run(orgId, name, bankName, routing);146  res.status(201).json({ id: result.lastInsertRowid });147});148149app.post("/transfers", requireRole(["admin", "manager"]), (req, res) => {150  const { orgId, accountId, beneficiaryId, amount, currency, memo } = req.body;151  const result = db152    .prepare(153      "insert into transfers (org_id, account_id, beneficiary_id, amount, currency, status, memo, requested_by) values (?, ?, ?, ?, ?, ?, ?, ?)"154    )155    .run(orgId, accountId, beneficiaryId, amount, currency, "queued", memo || "", req.identity?.userId || "unknown");156  res.status(201).json({ id: result.lastInsertRowid, status: "queued" });157});158159app.post("/documents/upload", requireRole(["admin", "manager"]), upload.single("file"), (req, res) => {160  const file = req.file;161  if (!file) {162    res.status(400).json({ error: "missing file" });163    return;164  }165166  const targetName = `${Date.now()}-${file.originalname}`;167  fs.copyFileSync(file.path, storagePath(targetName));168  const result = db169    .prepare(170      "insert into documents (org_id, filename, original_name, mime_type) values (?, ?, ?, ?)"171    )172    .run(req.identity?.orgId || 1, targetName, file.originalname, file.mimetype);173174  res.status(201).json({ id: result.lastInsertRowid, filename: targetName });175});176177app.get("/documents/:name", requireRole(["admin", "manager", "viewer"]), (req, res) => {178  const filePath = storagePath(req.params.name);179  if (!fs.existsSync(filePath)) {180    res.status(404).json({ error: "not found" });181    return;182  }183  res.setHeader("content-type", "application/octet-stream");184  res.send(fs.readFileSync(filePath));185});186187app.get("/reports/org-summary", requireRole(["admin", "manager"]), (req, res) => {188  const orgId = Number(req.query.orgId || req.identity?.orgId || 1);189  const org = db.prepare("select * from organizations where id = ?").get(orgId);190  const transfers = db191    .prepare("select * from transfers where org_id = ? order by created_at desc limit 25")192    .all(orgId);193  const title = typeof req.query.title === "string" ? req.query.title : "Treasury Summary";194  const html = `195    <html>196      <body>197        <h1>${title}</h1>198        <h2>${org?.name ?? "Unknown org"}</h2>199        <p>Region: ${org?.region ?? "n/a"} Tier: ${org?.tier ?? "n/a"}</p>200        <ul>201          ${transfers202            .map(203              (transfer) =>204                `<li>${transfer.currency} ${transfer.amount} - ${transfer.status} - ${transfer.memo}</li>`205            )206            .join("")}207        </ul>208      </body>209    </html>210  `;211  res.type("html").send(html);212});213214app.get("/audit", requireRole(["admin"]), (req, res) => {215  const orgId = typeof req.query.orgId === "string" ? Number(req.query.orgId) : req.identity?.orgId;216  const rows = db217    .prepare("select * from audit_records where org_id = ? order by created_at desc limit 100")218    .all(orgId);219  res.json({ data: rows });220});221222app.use((_req, res) => {223  res.status(404).json({ error: "not found" });224});225226const port = Number(process.env.PORT || 4100);227app.listen(port, () => {228  console.log(`portal listening on ${port}`);229});

After remediation

1import express, { NextFunction, Request, Response } from "express";2import fs from "fs";3import path from "path";4import multer from "multer";5import helmet from "helmet";6import { db, recordAudit, storagePath } from "./store.js";78type Identity = {9  orgId: number;10  userId: string;11  role: "admin" | "manager" | "viewer";12};1314declare global {15  namespace Express {16    interface Request {17      identity?: Identity;18    }19  }20}2122const app = express();23const upload = multer({ dest: path.resolve(__dirname, "..", "tmp") });2425fs.mkdirSync(path.resolve(__dirname, "..", "tmp"), { recursive: true });2627app.use(express.json());28app.use(express.urlencoded({ extended: true }));29app.use(helmet());3031app.use((req, res, next) => {32  const orgId = Number(req.header("x-org-id") || "1");33  const userId = req.header("x-user-id") || "portal-user";34  const role = (req.header("x-role") || "viewer") as Identity["role"];35  req.identity = { orgId, userId, role };36  next();37});3839app.use((req, res, next) => {40  const finish = () => {41    if (!req.identity) {42      return;43    }44    recordAudit({45      orgId: req.identity.orgId,46      actor: req.identity.userId,47      action: `${req.method} ${req.path}`,48      resource: req.originalUrl,49      details: {50        statusCode: res.statusCode,51        query: req.query,52        body: req.body53      }54    });55  };56  res.on("finish", finish);57  next();58});5960function requireRole(allowed: Identity["role"][]) {61  return (req: Request, res: Response, next: NextFunction) => {62    if (!req.identity || !allowed.includes(req.identity.role)) {63      res.status(403).json({ error: "forbidden" });64      return;65    }66    next();67  };68}6970function buildOrgFilters(query: Request["query"]) {71  const clauses: string[] = [];72  const params: unknown[] = [];7374  if (typeof query.name === "string" && query.name.trim()) {75    clauses.push("name like ?");76    params.push(`%${query.name.trim()}%`);77  }78  if (typeof query.region === "string" && query.region.trim()) {79    clauses.push("region = ?");80    params.push(query.region.trim());81  }82  if (typeof query.tier === "string" && query.tier.trim()) {83    clauses.push("tier = ?");84    params.push(query.tier.trim());85  }8687  const where = clauses.length ? ` where ${clauses.join(" and ")}` : "";88  return { where, params };89}9091app.get("/health", (_req, res) => {92  const counts = {93    organizations: db.prepare("select count(*) as count from organizations").get().count,94    transfers: db.prepare("select count(*) as count from transfers").get().count,95    auditRecords: db.prepare("select count(*) as count from audit_records").get().count96  };97  res.json({ ok: true, counts });98});99100app.get("/orgs", requireRole(["admin", "manager", "viewer"]), (req, res) => {101  const { where, params } = buildOrgFilters(req.query);102  const sqlParts = ["select * from organizations"];103  if (where) {104    sqlParts.push(where);105  }106  sqlParts.push("order by id desc");107  const rows = db.prepare(sqlParts.join(" ")).all(...params);108  res.json({ data: rows });109});110111app.post("/orgs", requireRole(["admin"]), (req, res) => {112  const { name, orgCode, region, tier } = req.body;113  const stmt = db.prepare(114    "insert into organizations (name, org_code, region, tier) values (?, ?, ?, ?)"115  );116  const result = stmt.run(name, orgCode, region, tier);117  res.status(201).json({ id: result.lastInsertRowid, name, orgCode, region, tier });118});119120app.get("/teams", requireRole(["admin", "manager"]), (req, res) => {121  const filters: string[] = [];122  const params: unknown[] = [];123124  if (typeof req.query.orgId === "string" && req.query.orgId.trim()) {125    filters.push("org_id = ?");126    params.push(Number(req.query.orgId));127  }128  if (typeof req.query.query === "string" && req.query.query.trim()) {129    filters.push("(name like ? or cost_center like ?)");130    params.push(`%${req.query.query.trim()}%`, `%${req.query.query.trim()}%`);131  }132133  const where = filters.length ? ` where ${filters.join(" and ")}` : "";134  const sqlParts = ["select * from teams"];135  if (where) {136    sqlParts.push(where);137  }138  sqlParts.push("order by created_at desc");139  const rows = db.prepare(sqlParts.join(" ")).all(...params);140  res.json({ data: rows });141});142143app.post("/users", requireRole(["admin", "manager"]), (req, res) => {144  const { orgId, teamId, email, displayName, role, status } = req.body;145  const result = db146    .prepare(147      "insert into users (org_id, team_id, email, display_name, role, status) values (?, ?, ?, ?, ?, ?)"148    )149    .run(orgId, teamId ?? null, email, displayName, role, status || "active");150  res.status(201).json({ id: result.lastInsertRowid, orgId, teamId, email, displayName, role });151});152153app.post("/beneficiaries", requireRole(["admin", "manager"]), (req, res) => {154  const { orgId, name, bankName, routing } = req.body;155  const result = db156    .prepare("insert into beneficiaries (org_id, name, bank_name, routing) values (?, ?, ?, ?)")157    .run(orgId, name, bankName, routing);158  res.status(201).json({ id: result.lastInsertRowid });159});160161app.post("/transfers", requireRole(["admin", "manager"]), (req, res) => {162  const { orgId, accountId, beneficiaryId, amount, currency, memo } = req.body;163  const result = db164    .prepare(165      "insert into transfers (org_id, account_id, beneficiary_id, amount, currency, status, memo, requested_by) values (?, ?, ?, ?, ?, ?, ?, ?)"166    )167    .run(orgId, accountId, beneficiaryId, amount, currency, "queued", memo || "", req.identity?.userId || "unknown");168  res.status(201).json({ id: result.lastInsertRowid, status: "queued" });169});170171app.post("/documents/upload", requireRole(["admin", "manager"]), upload.single("file"), (req, res) => {172  const file = req.file;173  if (!file) {174    res.status(400).json({ error: "missing file" });175    return;176  }177178  const targetName = `${Date.now()}-${file.originalname}`;179  let targetPath: string;180  try {181    targetPath = storagePath(targetName);182  } catch (_error) {183    res.status(400).json({ error: "invalid file name" });184    return;185  }186  fs.copyFileSync(file.path, targetPath);187  const result = db188    .prepare(189      "insert into documents (org_id, filename, original_name, mime_type) values (?, ?, ?, ?)"190    )191    .run(req.identity?.orgId || 1, targetName, file.originalname, file.mimetype);192193  res.status(201).json({ id: result.lastInsertRowid, filename: targetName });194});195196app.get("/documents/:name", requireRole(["admin", "manager", "viewer"]), (req, res) => {197  let filePath: string;198  try {199    filePath = storagePath(req.params.name);200  } catch (_error) {201    res.status(400).json({ error: "invalid file name" });202    return;203  }204  if (!fs.existsSync(filePath)) {205    res.status(404).json({ error: "not found" });206    return;207  }208  res.setHeader("content-type", "application/octet-stream");209  res.send(fs.readFileSync(filePath));210});211212app.get("/reports/org-summary", requireRole(["admin", "manager"]), (req, res) => {213  const orgId = Number(req.query.orgId || req.identity?.orgId || 1);214  const org = db.prepare("select * from organizations where id = ?").get(orgId);215  const transfers = db216    .prepare("select * from transfers where org_id = ? order by created_at desc limit 25")217    .all(orgId) as Array<{218    currency: string;219    amount: number;220    status: string;221    memo: string;222  }>;223  const title = typeof req.query.title === "string" ? req.query.title : "Treasury Summary";224  const html = `225    <html>226      <body>227        <h1>${title}</h1>228        <h2>${org?.name ?? "Unknown org"}</h2>229        <p>Region: ${org?.region ?? "n/a"} Tier: ${org?.tier ?? "n/a"}</p>230        <ul>231          ${transfers232            .map(233              (transfer) =>234                `<li>${transfer.currency} ${transfer.amount} - ${transfer.status} - ${transfer.memo}</li>`235            )236            .join("")}237        </ul>238      </body>239    </html>240  `;241  res.type("html").send(html);242});243244app.get("/audit", requireRole(["admin"]), (req, res) => {245  const orgId = typeof req.query.orgId === "string" ? Number(req.query.orgId) : req.identity?.orgId;246  const rows = db247    .prepare("select * from audit_records where org_id = ? order by created_at desc limit 100")248    .all(orgId);249  res.json({ data: rows });250});251252app.use((_req, res) => {253  res.status(404).json({ error: "not found" });254});255256const port = Number(process.env.PORT || 4100);257258if (require.main === module) {259  app.listen(port, () => {260    console.log(`portal listening on ${port}`);261  });262}263264export { app };