Merge branch 'master' into xss/jsImprove

This commit is contained in:
rugk
2026-02-23 16:48:21 +01:00
committed by GitHub
66 changed files with 1370 additions and 1202 deletions

View File

@@ -10,7 +10,7 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto;
// application libraries to test
global.$ = global.jQuery = require('./jquery-3.7.1');
global.zlib = require('./zlib-1.3.1-1').zlib;
global.zlib = require('./zlib-1.3.1-2').zlib;
require('./prettify');
global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR.prettyPrintOne;
@@ -82,10 +82,10 @@ function parseMime(line) {
// these to be character encoding agnostic
exports.atob = function(encoded) {
return Buffer.from(encoded, 'base64').toString('binary');
}
};
exports.btoa = function(text) {
return Buffer.from(text, 'binary').toString('base64');
}
};
// provides random lowercase characters from a to z
exports.jscA2zString = function() {

View File

@@ -55,12 +55,43 @@
* blacklist of UserAgents (parts) known to belong to a bot
*
* @private
* @enum {Array}
* @type {string[]}
* @readonly
*/
var badBotUA = [
// Generic bot identifiers
'bot',
'Bot',
'bot'
'crawler',
'Crawler',
'spider',
'Spider',
'scraper',
'Scraper',
// Search Engines
'Mediapartners-Google',
'BingPreview',
'Yahoo! Slurp',
// SEO & Analytics
'Screaming Frog',
// Social Media
'facebookexternalhit',
// AI & LLM
'ChatGPT-User',
'anthropic-ai',
// Security Scanners
'CensysInspect',
'Shodan',
// Other Common Crawlers
'80legs',
'ia_archiver',
'Teoma',
];
/**

17
js/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "privatebin",
"version": "2.0.0",
"version": "2.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "privatebin",
"version": "2.0.0",
"version": "2.0.3",
"license": "zlib-acknowledgement",
"devDependencies": {
"@peculiar/webcrypto": "^1.5.0",
@@ -1275,11 +1275,10 @@
"license": "ISC"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -2785,9 +2784,9 @@
"dev": true
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"requires": {
"argparse": "^2.0.1"

View File

@@ -569,13 +569,14 @@ jQuery.PrivateBin = (function($) {
* @param {string} str
* @return {string} escaped HTML
*/
me.htmlEntities = function(str) {
me.htmlEntities = function(str)
{
return String(str).replace(
/[&<>"'`=\/]/g, function(s) {
return entityMap[s];
}
);
}
};
/**
* calculate expiration date given initial date and expiration period
@@ -586,7 +587,8 @@ jQuery.PrivateBin = (function($) {
* @param {string|number} expirationDisplayStringOrSecondsToExpire - may not be empty
* @return {Date}
*/
me.calculateExpirationDate = function(initialDate, expirationDisplayStringOrSecondsToExpire) {
me.calculateExpirationDate = function(initialDate, expirationDisplayStringOrSecondsToExpire)
{
let expirationDate = new Date(initialDate),
secondsToExpiration = expirationDisplayStringOrSecondsToExpire;
if (typeof expirationDisplayStringOrSecondsToExpire === 'string') {
@@ -631,7 +633,7 @@ jQuery.PrivateBin = (function($) {
}
return result;
}
};
/**
* resets state, used for unit testing
@@ -685,7 +687,7 @@ jQuery.PrivateBin = (function($) {
* @prop {string[]}
* @readonly
*/
const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ro', 'ru', 'sk', 'sl', 'th', 'tr', 'uk', 'zh'];
const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ro', 'ru', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'zh'];
/**
* built in language
@@ -873,6 +875,7 @@ jQuery.PrivateBin = (function($) {
case 'sk':
return n === 1 ? 0 : (n >= 2 && n <= 4 ? 1 : 2);
case 'co':
case 'fa':
case 'fr':
case 'oc':
case 'tr':
@@ -896,7 +899,7 @@ jQuery.PrivateBin = (function($) {
return n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
case 'sl':
return n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0));
// bg, ca, de, el, en, es, et, fi, hu, it, nl, no, pt
// bg, ca, de, el, en, es, et, fi, hu, it, nl, no, pt, sv
default:
return n !== 1 ? 1 : 0;
}
@@ -1317,7 +1320,7 @@ jQuery.PrivateBin = (function($) {
spec[1] = atob(spec[1]);
if (spec[7] === 'zlib') {
if (typeof zlib === 'undefined') {
throw 'Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.'
throw 'Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.';
}
}
try {
@@ -2997,8 +3000,9 @@ jQuery.PrivateBin = (function($) {
attachmentLink.attr('download', fileName);
const fileSize = Helper.formatBytes(decodedData.length);
const fileInfo = document.createTextNode(` (${fileName}, ${fileSize})`);
template[0].appendChild(fileInfo);
const spans = template[0].querySelectorAll('span');
const span = spans[spans.length - 1];
span.textContent += ` (${fileName}, ${fileSize})`;
}
// sanitize SVG preview
@@ -3239,7 +3243,7 @@ jQuery.PrivateBin = (function($) {
* @param {FileList[]} loadedFiles (optional) loaded files array
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()}
*/
function readFileData(loadedFiles) {
function readFileData(loadedFiles = []) {
// Clear old cache
me.removeAttachmentData();
@@ -3251,15 +3255,15 @@ jQuery.PrivateBin = (function($) {
return;
}
if (loadedFiles === undefined) {
loadedFiles = [...$fileInput[0].files];
me.clearDragAndDrop();
} else {
if (loadedFiles && loadedFiles.length > 0) {
const fileNames = loadedFiles.map((loadedFile => loadedFile.name));
printDragAndDropFileNames(fileNames);
} else {
loadedFiles = [...$fileInput[0].files];
me.clearDragAndDrop();
}
if (typeof loadedFiles !== 'undefined') {
if (loadedFiles.length > 0) {
files = loadedFiles;
loadedFiles.forEach((loadedFile, index) => {
const fileReader = new FileReader();
@@ -3404,19 +3408,21 @@ jQuery.PrivateBin = (function($) {
* @function
*/
function addClipboardEventHandler() {
$('#message').on('paste', function (event) {
document.addEventListener('paste', (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
const files = [...items]
.filter(item => item.kind === 'file')
.map(item => item.getAsFile());
if (TopNav.isAttachmentReadonly()) {
if (TopNav.isAttachmentReadonly() && files.length) {
event.stopPropagation();
event.preventDefault();
return false;
}
readFileData(files);
if (files.length) {
readFileData(files);
}
});
}
@@ -4635,7 +4641,11 @@ jQuery.PrivateBin = (function($) {
*/
me.setFormat = function(format)
{
$formatter.parent().find(`a[data-format="${format}"]`).click();
if (Helper.isBootstrap5()) {
$formatter.find('select').val(format);
} else {
$formatter.parent().find(`a[data-format="${format}"]`).click();
}
}
/**
@@ -5795,6 +5805,10 @@ jQuery.PrivateBin = (function($) {
AttachmentViewer.removeAttachment();
TopNav.resetInput();
// reset format
PasteViewer.setFormat('plaintext');
TopNav.setFormat('plaintext');
TopNav.showCreateButtons();
// newPaste could be called when user is on document clone editing view

View File

@@ -49,7 +49,7 @@ describe('AttachmentViewer', function () {
{value: function(blob) {
return 'blob:' + location.origin + '/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed';
}}
)
);
}
$.PrivateBin.AttachmentViewer.init();
$.PrivateBin.Model.init();
@@ -152,7 +152,7 @@ describe('AttachmentViewer', function () {
{value: function(blob) {
return 'blob:' + location.origin + '/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed';
}}
)
);
}
$.PrivateBin.AttachmentViewer.init();
$.PrivateBin.Model.init();

View File

@@ -126,7 +126,7 @@ describe('Helper', function () {
clean();
urlString = $('<div />').text(urlString).html();
const expected = $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html();
return $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html() === result;
return expected === result;
}
);
jsc.property(

View File

@@ -144,7 +144,7 @@ describe('PasteStatus', function () {
common.jscUrl(),
common.jscUrl(false),
function (schema, longUrl, shortUrl) {
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
const [_, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
yourlsResponse = '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'\t<head>\n' +

View File

@@ -10,7 +10,7 @@ describe('Prompt', function () {
'string',
function (password) {
password = password.replace(/\r+|\n+/g, '');
const clean = jsdom('', {url: 'ftp://example.com/?0000000000000000'});
/* const clean = */ jsdom('', {url: 'ftp://example.com/?0000000000000000'});
$('body').html(
'<div id="passwordmodal" class="modal fade" role="dialog">' +
'<div class="modal-dialog"><div class="modal-content">' +

View File

@@ -1,5 +1,5 @@
'use strict';
var common = require('../common');
require('../common');
describe('TopNav', function () {
describe('showViewButtons & hideViewButtons', function () {
@@ -10,7 +10,7 @@ describe('TopNav', function () {
it(
'displays & hides navigation elements for viewing an existing document',
function () {
var results = [];
let results = [];
$('body').html(
'<nav class="navbar navbar-inverse navbar-static-top">' +
'<div id="navbar" class="navbar-collapse collapse"><ul ' +
@@ -69,7 +69,7 @@ describe('TopNav', function () {
it(
'displays & hides navigation elements for creating a document',
function () { // eslint-disable-line complexity
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li><button id="newbutton" ' +
'type="button" class="hidden">New</button></li><li><a ' +
@@ -134,7 +134,7 @@ describe('TopNav', function () {
it(
'displays the button for creating a document',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li><button id="newbutton" type=' +
'"button" class="hidden">New</button></li></ul></div></nav>'
@@ -165,7 +165,7 @@ describe('TopNav', function () {
it(
'hides the button for cloning a document',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li><button id="clonebutton" ' +
'type="button" class="btn btn-warning navbar-btn">' +
@@ -198,7 +198,7 @@ describe('TopNav', function () {
it(
'hides the raw text button',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li><button ' +
'id="rawtextbutton" type="button" class="btn ' +
@@ -232,7 +232,7 @@ describe('TopNav', function () {
it(
'hides the file attachment selection button',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' +
@@ -270,7 +270,7 @@ describe('TopNav', function () {
it(
'display the custom file attachment',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' +
@@ -308,8 +308,8 @@ describe('TopNav', function () {
it(
'collapses the navigation when displayed on a small screen',
function () {
var clean = jsdom(),
results = [];
const clean = jsdom();
let results = [];
$('body').html(
'<nav><div class="navbar-header"><button type="button" ' +
'class="navbar-toggle collapsed" data-toggle="collapse" ' +
@@ -363,7 +363,7 @@ describe('TopNav', function () {
it(
'reset inputs to defaults (options off)',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' +
@@ -407,7 +407,7 @@ describe('TopNav', function () {
it(
'reset inputs to defaults (burnafterreading on)',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' +
@@ -450,7 +450,7 @@ describe('TopNav', function () {
it(
'reset inputs to defaults (opendiscussion on)',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' +
@@ -517,16 +517,14 @@ describe('TopNav', function () {
cleanup();
});
var File = window.File,
const File = window.File,
FileList = window.FileList,
path = require('path'), // eslint-disable-line global-require
mime = require('mime-types'); // eslint-disable-line global-require
// mocking file input as per https://github.com/jsdom/jsdom/issues/1272
function createFile(file_path) {
var file = fs.statSync(file_path),
lastModified = file.mtimeMs,
size = file.size;
const lastModified = fs.statSync(file_path).mtimeMs;
return new File(
[new fs.readFileSync(file_path)],
@@ -559,7 +557,7 @@ describe('TopNav', function () {
it(
'returns the selected files',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' +
@@ -579,7 +577,7 @@ describe('TopNav', function () {
'../img/logo.svg',
'../img/busy.gif'
]);
var files = $.PrivateBin.TopNav.getFileList();
const files = $.PrivateBin.TopNav.getFileList();
results.push(
files[0].name === 'logo.svg' &&
files[1].name === 'busy.gif'
@@ -602,7 +600,7 @@ describe('TopNav', function () {
it(
'returns if the burn-after-reading checkbox was ticked',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' +
@@ -639,7 +637,7 @@ describe('TopNav', function () {
it(
'returns if the open-discussion checkbox was ticked',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="opendiscussionoption" ' +
'class="hidden"><label><input type="checkbox" ' +
@@ -678,7 +676,7 @@ describe('TopNav', function () {
'string',
function (password) {
password = password.replace(/\r+|\n+/g, '');
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li><div id="password" ' +
'class="navbar-form hidden"><input type="password" ' +
@@ -715,7 +713,7 @@ describe('TopNav', function () {
it(
'returns the custom attachment element',
function () {
var results = [];
let results = [];
$('body').html(
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' +

View File

@@ -1,6 +1,5 @@
'use strict';
var common = require('../common');
const assert = require('assert');
require('../common');
// DOM builder that mirrors bootstrap5.php navbar
function buildEmailDomNoShortUrl() {

View File

@@ -129,7 +129,7 @@
rawDef.destroy();
return ret;
},
}
};
return ret;
}