aboutsummaryrefslogtreecommitdiffstats
path: root/sampleconfig/lightning
diff options
context:
space:
mode:
authorRizwan Qaiser <[email protected]>2022-01-07 16:50:39 +0500
committerRizwan Qaiser <[email protected]>2022-01-07 16:50:39 +0500
commita874a2b956d6159d85095d4f71fad258d555a387 (patch)
treece529d00adb05fc2285aa19e9d045cebbf54345a /sampleconfig/lightning
parentad486f7828880c51a81958b14830da4f9c96e61f (diff)
downloadcoinos-server-a874a2b956d6159d85095d4f71fad258d555a387.tar.gz
coinos-server-a874a2b956d6159d85095d4f71fad258d555a387.zip
updated readme with new steps. updated docker-compose.dev.yml to adhere to new network requirements
Diffstat (limited to 'sampleconfig/lightning')
0 files changed, 0 insertions, 0 deletions
.highlight .na { color: #369 } /* Name.Attribute */ .highlight .nb { color: #038 } /* Name.Builtin */ .highlight .nc { color: #B06; font-weight: bold } /* Name.Class */ .highlight .no { color: #036; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555 } /* Name.Decorator */ .highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #06B; font-weight: bold } /* Name.Function */ .highlight .nl { color: #369; font-style: italic } /* Name.Label */ .highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #369; font-weight: bold } /* Name.Property */ .highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #369 } /* Name.Variable */ .highlight .ow { color: #080 } /* Operator.Word */ .highlight .w { color: #BBB } /* Text.Whitespace */ .highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */ .highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */ .highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */ .highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */ .highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */ .highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */ .highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */ .highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */ .highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */ .highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */ .highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */ .highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */ .highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */ .highlight .bp { color: #038 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #369 } /* Name.Variable.Class */ .highlight .vg { color: #D70 } /* Name.Variable.Global */ .highlight .vi { color: #33B } /* Name.Variable.Instance */ .highlight .vm { color: #369 } /* Name.Variable.Magic */ .highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
import { createHash } from "crypto";
import { writeFileSync } from "fs";
import { db } from "$lib/db";
import { err, warn } from "$lib/logging";
import { bail, fail } from "$lib/utils";
import { convert } from "$lib/image";
import { fileTypeFromBuffer } from "file-type";

// Only the formats whose decoders we are willing to expose to arbitrary
// authenticated input. Decoding happens in a throwaway child process
// (lib/image.ts) since 2026-09-26, when a stream of corrupt HEIF files
// crash-looped the server via heap corruption inside libheif —
// but a crashing decoder is still a decoder we'd rather not run at all.
// HEIF/AVIF/TIFF/SVG/PDF never get past the sniff. The previous check let
// any image/* subtype through.
const ALLOWED = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);

// Per-account budget. Avatars/banners change a handful of times, ever; the
// fuzzer was pushing 10+ a minute.
const RATE_MAX = 10;
const RATE_WINDOW_S = 600;

const clientIp = (req) => req.headers["cf-connecting-ip"] || req.ip;

export default async (req, res) => {
  const { user } = req;
  try {
    const {
      params: { type },
    } = req;

    // A `blacklist` freeze only blocks sends; the JWT still works everywhere
    // else, which is how a frozen account kept crashing us. Uploads are pure
    // attack surface for a frozen account, so refuse them outright.
    const uname = user?.username?.toLowerCase().trim();
    const blacklisted =
      (await db.sIsMember("blacklist", user.id)) ||
      (await db.sIsMember("blacklist", uname));
    const whitelisted = await db.sIsMember("whitelist", uname);
    if (blacklisted && !whitelisted) {
      warn(`UPLOAD_REJECTED frozen ${user.username} ${clientIp(req)}`);
      fail("unauthorized");
    }

    const rk = `upload:rl:${user.id}`;
    const n = await db.incr(rk);
    if (n === 1) await db.expire(rk, RATE_WINDOW_S);
    if (n > RATE_MAX) {
      warn(`UPLOAD_REJECTED ratelimit ${user.username} ${clientIp(req)} ${n}`);
      fail("too many uploads, try again later");
    }

    const data = await req.file();
    let buf = await data.toBuffer();

    const ft = await fileTypeFromBuffer(buf);
    if (!ft || !ALLOWED.has(ft.mime)) {
      warn(
        `UPLOAD_REJECTED type ${user.username} ${clientIp(req)} ${ft?.mime || "unknown"} ${buf.length}`,
      );
      fail("unsupported file type");
    }

    const w = type === "banner" ? 1920 : 240;
    buf = await convert(buf, w);

    const hash = createHash("sha256").update(buf).digest("hex");

    const filePath = `/home/bun/app/data/uploads/${hash}.webp`;
    writeFileSync(filePath, buf);

    res.send({ hash });
  } catch (e) {
    err("problem uploading", user?.username, e.message);
    bail(res, e.message);
  }
};