git repos and security
This commit is contained in:
parent
9495335171
commit
b369ce69da
15 changed files with 105 additions and 19 deletions
14
assets/401.html
Normal file
14
assets/401.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>401</title>
|
||||
<meta charset="utf8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/assets/style.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
<h1>401</h1>
|
||||
<p>you can't.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
/assets/ resources for the site itself
|
||||
/media/ images, videos, music, and other cool stuff i found
|
||||
/public/ random things that i have made public
|
||||
/public/snek/ snek!
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<body>
|
||||
<h1>index of {{dir}}</h1>
|
||||
{{#comment}}<p>{{comment}}</p>{{/comment}}
|
||||
{{#git}}<div class="git">this is a git repository! clone to see history</div>{{/git}}
|
||||
<table>
|
||||
<tr><th>name</th><th>date</th><th>size</th></tr>
|
||||
{{#files}}<tr><td><a href="{{link}}">{{name}}</a></td><td>{{date}}</td><td>{{size}}</td></tr>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const mustache = require("mustache");
|
||||
const { version } = require("../modules/cache.js");
|
||||
const { version } = require("../modules/03-cache.js");
|
||||
const template = fs.readFileSync("assets/directory.html", "utf8");
|
||||
|
||||
const comments = fs.readFileSync(path.join(__dirname, "../assets/comments"), "utf8")
|
||||
|
@ -23,6 +23,13 @@ exports.hash = (where) => {
|
|||
exports.render = (where, name) => {
|
||||
const files = [];
|
||||
const names = fs.readdirSync(where);
|
||||
let git = false;
|
||||
|
||||
if(names.includes(".git")) {
|
||||
git = true;
|
||||
if(!comments.has(where)) comments.set(name, fs.readFileSync(path.join(where, ".git/description"), "utf8"));
|
||||
names.splice(names.indexOf(".git"), 1);
|
||||
}
|
||||
|
||||
for(let file of names) {
|
||||
const stat = fs.statSync(path.join(where, file));
|
||||
|
@ -44,6 +51,7 @@ exports.render = (where, name) => {
|
|||
dir: name,
|
||||
files: sorted,
|
||||
comment: comments.get(name),
|
||||
git: git,
|
||||
cache: {
|
||||
style: version("/assets/style.css"),
|
||||
dir: version("/assets/directory.css"),
|
||||
|
|
|
@ -2,7 +2,7 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const mustache = require("mustache");
|
||||
const gemini = require("../util/gemini.js");
|
||||
const { version } = require("../modules/cache.js");
|
||||
const { version } = require("../modules/03-cache.js");
|
||||
|
||||
const template = fs.readFileSync("assets/wrapper.html", "utf8");
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const mustache = require("mustache");
|
||||
const { version } = require("../modules/cache.js");
|
||||
const { version } = require("../modules/03-cache.js");
|
||||
const md = require("../util/markdown.js");
|
||||
|
||||
const template = fs.readFileSync("assets/wrapper.html", "utf8").valueOf();
|
||||
|
|
2
index.js
2
index.js
|
@ -17,7 +17,7 @@ app.all("*", (req, res, next) => {
|
|||
next();
|
||||
});
|
||||
|
||||
for(let file of fs.readdirSync(dir + "/modules")) {
|
||||
for(let file of fs.readdirSync(dir + "/modules").sort()) {
|
||||
require(dir + "/modules/" + file)(app, dir, log);
|
||||
log.info(`loded module ${file}`);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
module.exports = (app, dir, log) => {
|
||||
const errorpage = fs.readFileSync(path.join(dir, "assets/401.html"));
|
||||
const authorize = (username, password) => async (req, res, next) => {
|
||||
if(req.headers["x-forwarded-proto"] !== "https") {
|
||||
res.writeHead(400).end("use https");
|
||||
|
@ -20,10 +23,15 @@ module.exports = (app, dir, log) => {
|
|||
}
|
||||
res.writeHead(401, {
|
||||
"WWW-Authenticate": 'Basic realm="secrets..."',
|
||||
}).end();
|
||||
}).end(errorpage);
|
||||
}
|
||||
};
|
||||
|
||||
// stop sneaky people
|
||||
app.get("/../*", (req, res) => res.writeHead(400).end("nope."));
|
||||
app.get("/./*", (req, res) => res.writeHead(400).end("nope."));
|
||||
|
||||
// secure the things
|
||||
app.get("/private/*", authorize(process.env.SECURE_USER, process.env.SECURE_PASS));
|
||||
app.get("/private/", authorize(process.env.SECURE_USER, process.env.SECURE_PASS));
|
||||
app.get("/restrict/", authorize(process.env.RESTRICT_USER, process.env.RESTRICT_PASS));
|
|
@ -1,19 +1,14 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const types = require("../types.js");
|
||||
const getType = (name) => types.get(path.extname(name).slice(1));
|
||||
const file = require("../static.js");
|
||||
|
||||
module.exports = (app, dir, log) => {
|
||||
const redir = (to) => (req, res, next) => req.path === to ? next() : res.redirect(to);
|
||||
const file = (name) => {
|
||||
const data = fs.readFileSync(path.join(dir, "overlay", name));
|
||||
return (req, res) => res.writeHead(200, { "Content-Type": getType(name) }).end(data);
|
||||
};
|
||||
|
||||
app.get("/", file("index.html"));
|
||||
app.get("/stats", file("stats.html"));
|
||||
app.get("/nft", file("no.html"));
|
||||
app.get("/unlist", (req, res) => res.redirect("/nope"));
|
||||
app.get("/unlist", redir("/unlist/"), file("unlist/unlist.html"));
|
||||
app.get("/cinny", redir("/cinny/"), file("/cinny/index.html"));
|
||||
};
|
||||
|
18
modules/04-skid.js
Normal file
18
modules/04-skid.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
module.exports = (app) => {
|
||||
const keepalive = (req, res) => {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": "5",
|
||||
})
|
||||
const interval = setInterval(() => {
|
||||
try {
|
||||
res.write("a");
|
||||
} catch {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
app.get("*.php", keepalive);
|
||||
app.get("*.aspx", keepalive);
|
||||
}
|
39
modules/05-git.js
Normal file
39
modules/05-git.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const gen = require("../static.js");
|
||||
const gitdirs = new Map();
|
||||
|
||||
module.exports = (app, dir, log) => {
|
||||
const isgit = (name) => {
|
||||
if(gitdirs.has(name)) return gitdirs.get(name);
|
||||
const exists = fs.existsSync(path.join(dir, "overlay", name, ".git"));
|
||||
gitdirs.set(name, exists);
|
||||
return exists;
|
||||
};
|
||||
|
||||
const mapgit = (dir) => (req, res, next) => {
|
||||
const name = path.normalize(req.params[0]);
|
||||
if(!isgit(name)) return next();
|
||||
gen(path.join(req.params[0], ".git", dir, req.params[1] || ""), true)(req, res);
|
||||
};
|
||||
|
||||
const services = ["git-upload-pack", "git-receive-pack"];
|
||||
app.get("*/info/refs", (req, res, next) => {
|
||||
if(!services.includes(req.query.service)) return next();
|
||||
const repo = path.join(dir, "overlay", path.normalize(req.params[0]));
|
||||
if(!fs.existsSync(repo)) return next();
|
||||
if(!fs.existsSync(path.join(repo, ".git"))) return res.sendStatus(400);
|
||||
log.info(`git ${req.query.service === "git-upload-pack" ? "pulling" : "pushing"} ${req.params[0]}`);
|
||||
let refs = "";
|
||||
for(let dir of fs.readdirSync(path.join(repo, ".git/refs"))) {
|
||||
for(let file of fs.readdirSync(path.join(repo, `.git/refs/${dir}`))) {
|
||||
refs += `${fs.readFileSync(path.join(repo, `.git/refs/${dir}`, file), "utf8").trim()}\trefs/${dir}/${file}\n`;
|
||||
}
|
||||
}
|
||||
res.end(refs);
|
||||
});
|
||||
|
||||
app.get("*/objects/*", mapgit("objects"));
|
||||
app.get("*/HEAD", mapgit("HEAD"));
|
||||
// app.propfind("*", console.log)
|
||||
}
|
16
static.js
16
static.js
|
@ -2,7 +2,7 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const types = require("./types.js");
|
||||
const log = require("./log.js");
|
||||
const { version } = require("./modules/cache.js");
|
||||
const { version } = require("./modules/03-cache.js");
|
||||
const mustache = require("mustache");
|
||||
const LRU = require("lru-cache");
|
||||
|
||||
|
@ -17,7 +17,7 @@ const handlers = {
|
|||
file: require("./handlers/file.js"),
|
||||
};
|
||||
|
||||
function generate(file, raw) {
|
||||
function generate(file, raw = false) {
|
||||
const where = path.resolve(__dirname + "/overlay/" + file);
|
||||
|
||||
if(!fs.existsSync(where)) {
|
||||
|
@ -35,7 +35,7 @@ function generate(file, raw) {
|
|||
return send("text/html", run(handlers.gemini));
|
||||
} else {
|
||||
const type = types.get(path.extname(where).slice(1));
|
||||
if(stat.size < 1024 * 1024 * 8) {
|
||||
if(stat.size < 1024 * 1024 * 1) {
|
||||
return send(type, run(handlers.file));
|
||||
} else {
|
||||
return (req, res) => {
|
||||
|
@ -49,10 +49,10 @@ function generate(file, raw) {
|
|||
};
|
||||
if(range) {
|
||||
if(range.type !== "bytes") return res.sendStatus(400);
|
||||
headers["Content-Range"] = `bytes ${range[0].start}-${range[0].end}`;
|
||||
headers["Content-Length"] = Math.abs(range[0].end - range[0].start);
|
||||
headers["Content-Range"] = `bytes ${range[0].start}-${range[0].end}/${stat.size}`;
|
||||
headers["Content-Length"] = Math.abs(range[0].end - range[0].start) + 1;
|
||||
log.info(`sending large file (${headers["Content-Range"]})`);
|
||||
res.writeHead(200, headers);
|
||||
res.writeHead(206, headers);
|
||||
fs.createReadStream(where, range[0]).pipe(res);
|
||||
} else {
|
||||
log.info("sending large file");
|
||||
|
@ -64,6 +64,7 @@ function generate(file, raw) {
|
|||
}
|
||||
|
||||
function send(type, content, status = 200) {
|
||||
content = Buffer.from(content);
|
||||
const headers = { "Content-Type": type || "text/plain", "Content-Length": content.length };
|
||||
return (req, res) => res.writeHead(status, headers).end(content);
|
||||
}
|
||||
|
@ -82,6 +83,9 @@ function generate(file, raw) {
|
|||
|
||||
module.exports = generate;
|
||||
module.exports.handle = (req, res) => {
|
||||
// stop sneaky people
|
||||
if(req.path.includes("..")) return res.writeHead(400).end("nope.");
|
||||
|
||||
generate(path.normalize(req.path), "raw" in req.query)(req, res);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue