matrixssbook/xssbook.js

123 lines
3 KiB
JavaScript
Raw Normal View History

2023-05-25 02:04:06 +00:00
const EventEmitter = require("events");
module.exports.XSSBook = class XSSBook extends EventEmitter {
constructor({ url, bridge_username, room }) {
super();
this.url = url;
this.bridge_username = bridge_username;
this.room = room;
this.tokens = {};
this.posts = {};
this.pollLoop();
}
async api(url, method, data, token) {
return await fetch(this.url + url, {
method,
headers: {
"Cookie": token ? "auth=" + encodeURIComponent(token) : undefined,
"Content-Type": data ? "application/json" : undefined,
},
body: data ? JSON.stringify(data) : undefined,
});
}
async getUserToken(username) {
if(username in this.tokens) {
return this.tokens[username];
}
let req = await this.api("/api/auth/register", "POST", {
firstname: username,
lastname: username === this.bridge_username ? "[bridge]" : "[matrix]",
email: username,
password: username,
gender: username === this.bridge_username ? "matrix bridge" : "matrix user",
day: 1,
month: 1,
year: 1970,
});
if(req.ok) {
const token = decodeURIComponent(/auth=([^;]+);/.exec(req.headers.get("set-cookie"))[1]);
return this.tokens[username] = token;
}
console.log("couldn't register " + username + ":", await req.text());
req = await this.api("/api/auth/login", "POST", {
email: username,
password: username,
});
if(req.ok) {
const token = decodeURIComponent(/auth=([^;]+);/.exec(req.headers.get("set-cookie"))[1]);
return this.tokens[username] = token;
}
console.log("couldn't login " + username + ":", await req.text());
throw new Error("Couldn't get token for " + username);
}
async apiUser(url, method, data, username) {
const token = await this.getUserToken(username);
return await this.api(url, method, data, token);
}
async getUsers(ids) {
const req = await this.apiUser("/api/users/load", "POST", {
ids,
}, this.bridge_username);
return await req.json();
}
async post(username, content) {
const res = await this.apiUser("/api/posts/create", "POST", {
content,
}, username);
return (await res.json()).post_id;
}
async pollLoop() {
let latest_post_id = 0, first = true;
while(true) {
await new Promise(r => setTimeout(r, 5000));
const req = await this.apiUser("/api/posts/page", "POST", {
page: 0,
}, this.bridge_username);
const posts = await req.json();
if(!Array.isArray(posts) || !posts.length) {
continue;
}
const new_posts = posts.filter(n => n.post_id > latest_post_id);
if(!new_posts.length) {
continue;
}
latest_post_id = Math.max(latest_post_id, ...new_posts.map(n => n.post_id));
if(first) { // ignore posts from the first sync
first = false;
continue;
}
const users_list = await this.getUsers(new_posts.map(n => n.user_id));
const users = {};
users_list.forEach(user => {
users[user.user_id] = user;
});
new_posts.forEach(post => {
const user = users[post.user_id];
this.emit("post", post, user);
});
}
}
}