edgeconnect-cryptpad-deploy.../www/report/main.js
Richard Robert Reitz addc3725ea
Some checks failed
build / build (push) Has been cancelled
first commit
2025-08-28 15:13:14 +02:00

409 lines
16 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
define([
'jquery',
'/api/config',
'/common/hyperscript.js',
'/customize/messages.js',
'/components/nthen/index.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-constants.js',
'/common/cryptget.js',
'/common/cryptpad-common.js',
'/common/cache-store.js',
'/common/common-interface.js',
'chainpad-netflux',
'/components/chainpad-crypto/crypto.js',
'/common/user-object.js',
'/common/clipboard.js',
'/common/outer/login-block.js',
//'/common/outer/roster.js',
'/common/rpc.js',
'/common/pinpad.js',
'/common/outer/local-store.js',
'/components/tweetnacl/nacl-fast.min.js',
'css!/components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/pages/page-report.less',
], function ($, ApiConfig, h, Messages,
nThen, Hash, Util, Constants, Crypt, Cryptpad, Cache, UI, CPNetflux,
Crypto, UserObject, Clipboard, Block, /*Roster,*/ Rpc, Pinpad, LocalStore) {
var $report = $('#cp-report');
var blockHash = localStorage.Block_hash;
if (!blockHash) {
return void UI.alert(Messages.mustLogin, function () {
var href = Hash.hashToHref('', 'login');
var url = Hash.getNewPadURL(href, {
href: '/report/',
});
console.log(url);
window.location.href = url;
});
}
let report = true;
let fixRoster = false;
let urlHash = window.location.hash.slice(1);
if (urlHash) {
let s = urlHash.split('=');
let key = s[0];
let value = s[1];
if (key === "roster") {
report = false;
fixRoster = value; // teamid
}
}
var addReport = function (str) {
$report.append(h('div', str));
};
var getReportContent = window.getReportContent = function () {
try {
return $report[0].innerText;
} catch (err) {
return '';
}
};
var copyToClipboard = function () {
Clipboard.copy(getReportContent(), (err) => {
if (err) { return UI.warn(Messages.error); }
UI.log(Messages.genericCopySuccess);
});
};
var checkCache = function (chan, cb) {
Cache.getChannelCache(chan, function (err, val) {
if (err) {
addReport('Cache error:' + err);
} else {
addReport('Cache: ' + val.c.length + ' entries');
}
cb();
});
};
var onCacheReady = function (info) {
var doc;
try {
doc = info.realtime.getUserDoc();
JSON.parse(doc);
addReport('Cache ready success: ' + info.id + ' - Length: ' + doc.length);
} catch (e) {
addReport('Cache ready error: ' + info.id + ' - Length: ' + (doc || '').length);
}
};
var network;
var proxy;
var hash;
nThen(function (waitFor) {
let done = waitFor();
var parsed = Block.parseBlockHash(blockHash);
var sessionToken = LocalStore.getSessionToken() || undefined;
Util.getBlock(parsed.href, {
bearer: sessionToken
}, waitFor((err, response) => {
if (err) {
waitFor.abort();
return void UI.alert('Invalid user');
}
response.arrayBuffer().then(arraybuffer => {
arraybuffer = new Uint8Array(arraybuffer);
// use the results to load your user hash and
// put your userhash into localStorage
try {
var block_info = Block.decrypt(arraybuffer, parsed.keys);
if (!block_info) { return console.error("Failed to decrypt !"); }
hash = block_info[Constants.userHashKey];
if (!hash) {
return void console.error("failed to decrypt or decode block content");
}
} catch (e) {
return void console.error("failed to decrypt or decode block content");
}
done();
});
}));
}).nThen(function (waitFor) {
Cryptpad.makeNetwork(waitFor(function (err, _network) {
if (err) {
console.error(err);
waitFor.abort();
return void UI.errorLoadingScreen(err);
}
network = _network;
}));
}).nThen(function (waitFor) {
addReport('BEGIN REPORT');
var secret = Hash.getSecrets('drive', hash);
addReport('Load drive. ID: ' + secret.channel);
checkCache(secret.channel, waitFor());
}).nThen(function (waitFor) {
Crypt.get(hash, waitFor(function (err, val) {
if (err) {
console.error(err);
addReport('Load drive error. err: ' + err);
return void waitFor.abort();
}
try {
proxy = JSON.parse(val);
} catch (e) {
console.error(e);
addReport('Load drive error. Parse error: ' + e);
waitFor.abort();
}
}), { network: network, onCacheReady: onCacheReady});
}).nThen(function (waitFor) {
var drive = proxy.drive || {};
if (!proxy.drive) {
addReport('ERROR: no drive');
return void waitFor.abort();
}
addReport('Load drive success.');
addReport('Public key: '+proxy.edPublic);
addReport('Shared folders: ' + Object.keys(drive.sharedFolders || {}).join(', '));
addReport('Teams: ' + Object.keys(proxy.teams || {}).join(', '));
addReport('-------------------');
if (!report) { return; }
var n = nThen;
Object.keys(drive.sharedFolders || {}).forEach(function (id) {
n = n(function (w) {
var next = w();
var obj = drive.sharedFolders[id];
addReport('Load shared folder. ID: ' + id + '. Channel ID: '+ obj.channel);
if (obj.password) { addReport("Password protected"); }
if (!obj.href) { addReport("View only"); }
checkCache(obj.channel, function () {
var parsed = Hash.parsePadUrl(obj.href || obj.roHref);
Crypt.get(parsed.hash, function (err, val, errorObj) {
if (err) {
addReport('ERROR: ' + err);
if (err === "ERESTRICTED") {
addReport('RESTRICTED: ' + (errorObj && errorObj.message));
}
} else {
addReport('Load shared folder: success. Size: ' + val.length);
}
addReport('-------------------');
next();
}, {
network: network,
password: obj.password,
onCacheReady: onCacheReady
});
});
}).nThen;
});
n(waitFor());
}).nThen(function () {
addReport('===================');
var n = nThen;
Object.keys(proxy.teams || {}).forEach(function (id) {
// If we're in "repair" mode, only load the affected team
var obj = proxy.teams[id];
if (!report && fixRoster !== obj.channel) { return; }
n = n(function (w) {
var next = w();
var team;
addReport('Load team. ID: ' + id + '. Channel ID: '+ obj.channel);
addReport('Roster channel: ' + obj.keys.roster.channel);
addReport('Roster lkh: ' + obj.keys.roster.lastKnownHash);
if (!obj.hash) { addReport("View only"); }
var teamSecret = Hash.getSecrets('team', obj.hash || obj.roHash, obj.password);
var cryptor = UserObject.createCryptor(teamSecret.keys.secondaryKey);
// Repair roster mode
/*
if (!report) {
let roster = obj.keys.roster;
let rpc, anon_rpc;
if (!obj.owner || !roster.edit) {
return void addReport('Roster error: only owners can repair');
}
n(w => {
Rpc.createAnonymous(network, w(function (e, call) {
anon_rpc = call;
}));
Pinpad.create(network, proxy, w(function (e, call) {
rpc = call;
}));
}).nThen(w => {
if (!anon_rpc || !rpc) {
return void addReport('RPC error');
}
var rosterKeys = Crypto.Team.deriveMemberKeys(roster.edit, {
curvePublic: proxy.curvePublic,
curvePrivate: proxy.curvePrivate
});
let store = { anon_rpc };
Roster.create({
network: network,
channel: roster.channel,
keys: rosterKeys,
store: store,
lastKnownHash: roster.lastKnownHash
}, w(function (err, _roster) {
if (err) { return void addReport('Fix roster error', err); }
_roster.checkpoint(w((err, state, hash) => {
if (err) { addReport('Fix roster error', err); }
rpc.trimHistory({
channel: roster.channel,
hash
}, w(function (err) {
console.error(arguments);
if (err) { addReport('Trim roster error', err); }
}));
}));
}));
}).nThen(next);
return;
}*/
// Check team drive
nThen(function (ww) {
addReport('Team drive');
var _next = ww();
checkCache(obj.channel, function () {
Crypt.get(obj.hash || obj.roHash, function (err, val) {
if (err) {
addReport('ERROR: ' + err);
addReport('===================');
next();
ww.abort();
} else {
addReport('Team drive success. Size: ' + val.length);
try {
team = JSON.parse(val);
} catch (e) {
addReport('PARSE ERROR: ' + e);
addReport('===================');
next();
ww.abort();
}
addReport('Shared folders: ' + Object.keys(team.drive.sharedFolders || {}).join(', '));
}
addReport('-------------------');
_next();
}, {
network: network,
password: obj.password,
onCacheReady: onCacheReady
});
});
}).nThen(function (ww) {
var _next = ww();
var d = Util.find(obj, ['keys', 'roster']);
var rosterKeys = d.edit ? Crypto.Team.deriveMemberKeys(d.edit, proxy)
: Crypto.Team.deriveGuestKeys(d.view || '');
if (d.channel !== rosterKeys.channel) {
next();
ww.abort();
addReport("Invalid roster keys:", d.channel, rosterKeys.channel);
return;
}
addReport('Roster channel: ' + d.channel);
checkCache(d.channel, function () {
var crypto = Crypto.Team.createEncryptor(rosterKeys);
var m = 0;
CPNetflux.start({
lastKnownHash: d.lastKnownHash || -1,
network: network,
channel: d.channel,
crypto: crypto,
validateKey: rosterKeys.teamEdPublic,
Cache: Cache,
noChainPad: true,
onCacheReady: onCacheReady,
onChannelError: function (obj) {
addReport('ERROR:' + obj.error);
if (obj.error === "ERESTRICTED") {
addReport('RESTRICTED: ' + obj.message);
}
next();
ww.abort();
addReport('===================');
return;
},
onMessage: function () {
m++;
},
onReady: function () {
addReport("Roster success. Length: "+m);
addReport('-------------------');
_next();
}
});
});
}).nThen(function (ww) {
var _next = ww();
var n = nThen;
var drive = team.drive;
Object.keys(drive.sharedFolders || {}).forEach(function (id) {
n = n(function (w) {
var next = w();
var _obj = drive.sharedFolders[id];
addReport('Load shared folder. ID: ' + id + '. Channel ID: '+ _obj.channel);
if (_obj.password) { addReport("Password protected"); }
if (!_obj.href) { addReport("View only"); }
checkCache(_obj.channel, function () {
if (_obj.href && _obj.href.indexOf('#') === -1) {
_obj.href = cryptor.decrypt(_obj.href);
}
var parsed = Hash.parsePadUrl(_obj.href || _obj.roHref);
Crypt.get(parsed.hash, function (err, val, errorObj) {
if (err) {
addReport('ERROR: ' + err);
if (err === "ERESTRICTED") {
addReport('RESTRICTED: ' + (errorObj && errorObj.message));
}
} else {
addReport('Load shared folder: success. Size: ' + val.length);
}
addReport('-------------------');
next();
}, {
network: network,
password: _obj.password,
onCacheReady: onCacheReady
});
});
}).nThen;
});
n(_next);
}).nThen(next);
}).nThen;
});
n(function () {
addReport('===================');
addReport('DONE');
var copyButton = h('button.btn.btn-primary', Messages.copyToClipboard);
copyButton.onclick = copyToClipboard;
var buttonContainer = h('div#cp-report-ui', [
copyButton,
]);
document.body.appendChild(buttonContainer);
});
});
});