Merge branch 'master' into ask-before-burn

This commit is contained in:
El RIDO 2024-01-27 18:56:12 +01:00
commit 03d2291ec7
No known key found for this signature in database
GPG Key ID: 0F5C940A6BD81F92
10 changed files with 272 additions and 210 deletions

View File

@ -41,7 +41,7 @@ jobs:
key: ${{ runner.os }}-${{ env.extensions-cache-key }} key: ${{ runner.os }}-${{ env.extensions-cache-key }}
- name: Cache extensions - name: Cache extensions
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ${{ steps.extcache.outputs.dir }} path: ${{ steps.extcache.outputs.dir }}
key: ${{ steps.extcache.outputs.key }} key: ${{ steps.extcache.outputs.key }}
@ -76,7 +76,7 @@ jobs:
shell: bash shell: bash
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ${{ steps.composer-cache.outputs.dir }} path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }} key: ${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }}

66
composer.lock generated
View File

@ -316,16 +316,16 @@
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.17.1", "version": "v4.18.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -366,9 +366,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0"
}, },
"time": "2023-08-13T19:53:39+00:00" "time": "2023-12-10T21:03:43+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@ -483,23 +483,23 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.29", "version": "9.2.30",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089",
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"nikic/php-parser": "^4.15", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=7.3", "php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3", "phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2", "phpunit/php-text-template": "^2.0.2",
@ -549,7 +549,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30"
}, },
"funding": [ "funding": [
{ {
@ -557,7 +557,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-09-19T04:57:46+00:00" "time": "2023-12-22T06:47:57+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -802,16 +802,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "9.6.15", "version": "9.6.16",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3767b2c56ce02d01e3491046f33466a1ae60a37f",
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -885,7 +885,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.16"
}, },
"funding": [ "funding": [
{ {
@ -901,7 +901,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-01T16:55:19+00:00" "time": "2024-01-19T07:03:14+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@ -1146,20 +1146,20 @@
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",
"version": "2.0.2", "version": "2.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git", "url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88" "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.7", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=7.3" "php": ">=7.3"
}, },
"require-dev": { "require-dev": {
@ -1191,7 +1191,7 @@
"homepage": "https://github.com/sebastianbergmann/complexity", "homepage": "https://github.com/sebastianbergmann/complexity",
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues", "issues": "https://github.com/sebastianbergmann/complexity/issues",
"source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
}, },
"funding": [ "funding": [
{ {
@ -1199,7 +1199,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2020-10-26T15:52:27+00:00" "time": "2023-12-22T06:19:30+00:00"
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
@ -1473,20 +1473,20 @@
}, },
{ {
"name": "sebastian/lines-of-code", "name": "sebastian/lines-of-code",
"version": "1.0.3", "version": "1.0.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git", "url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.6", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=7.3" "php": ">=7.3"
}, },
"require-dev": { "require-dev": {
@ -1518,7 +1518,7 @@
"homepage": "https://github.com/sebastianbergmann/lines-of-code", "homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
}, },
"funding": [ "funding": [
{ {
@ -1526,7 +1526,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2020-11-28T06:42:11+00:00" "time": "2023-12-22T06:20:34+00:00"
}, },
{ {
"name": "sebastian/object-enumerator", "name": "sebastian/object-enumerator",

View File

@ -113,8 +113,8 @@ exports.jscBase64String = function() {
}; };
// provides a random URL schema supported by the whatwg-url library // provides a random URL schema supported by the whatwg-url library
exports.jscSchemas = function() { exports.jscSchemas = function(withFtp = true) {
return jsc.elements(schemas); return jsc.elements(withFtp ? schemas : schemas.slice(1));
}; };
// provides a random supported language string // provides a random supported language string
@ -131,3 +131,24 @@ exports.jscMimeTypes = function() {
exports.jscFormats = function() { exports.jscFormats = function() {
return jsc.elements(formats); return jsc.elements(formats);
}; };
// provides random URLs
exports.jscUrl = function(withFragment = true, withQuery = true) {
let url = {
schema: exports.jscSchemas(),
address: jsc.nearray(exports.jscA2zString()),
};
if (withFragment) {
url.fragment = jsc.string;
}
if(withQuery) {
url.query = jsc.array(exports.jscQueryString());
}
return jsc.record(url);
};
exports.urlToString = function (url) {
return url.schema + '://' + url.address.join('') + '/' + (url.query ? '?' +
encodeURI(url.query.join('').replace(/^&+|&+$/gm,'')) : '') +
(url.fragment ? '#' + encodeURI(url.fragment) : '');
};

View File

@ -2128,7 +2128,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
response = JSON.stringify(response); response = JSON.stringify(response);
} }
if (typeof response === 'string' && response.length > 0) { if (typeof response === 'string' && response.length > 0) {
const shortUrlMatcher = /https?:\/\/[^\s]+/g; const shortUrlMatcher = /https?:\/\/[^\s"<]+/g; // JSON API will have URL in quotes, XML in tags
const shortUrl = (response.match(shortUrlMatcher) || []).filter(function(urlRegExMatch) { const shortUrl = (response.match(shortUrlMatcher) || []).filter(function(urlRegExMatch) {
if (typeof URL.canParse === 'function') { if (typeof URL.canParse === 'function') {
return URL.canParse(urlRegExMatch); return URL.canParse(urlRegExMatch);
@ -2140,7 +2140,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return false; return false;
} }
}).sort(function(a, b) { }).sort(function(a, b) {
return a.length - b.length; return a.length - b.length; // shortest first
})[0]; })[0];
if (typeof shortUrl === 'string' && shortUrl.length > 0) { if (typeof shortUrl === 'string' && shortUrl.length > 0) {
// we disable the button to avoid calling shortener again // we disable the button to avoid calling shortener again

View File

@ -96,36 +96,34 @@ describe('Helper', function () {
jsc.property( jsc.property(
'replaces URLs with anchors', 'replaces URLs with anchors',
'string', 'string',
jsc.elements(['http', 'https', 'ftp']), common.jscUrl(),
jsc.nearray(common.jscA2zString()),
jsc.array(common.jscQueryString()),
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
'string', 'string',
function (prefix, schema, address, query, fragment, postfix) { function (prefix, url, fragment, postfix) {
query = query.join('');
fragment = fragment.join('');
prefix = prefix.replace(/\r|\f/g, '\n').replace(/\u0000/g, '').replace(/\u000b/g, ''); prefix = prefix.replace(/\r|\f/g, '\n').replace(/\u0000/g, '').replace(/\u000b/g, '');
postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, ''); postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, '');
let url = schema + '://' + address.join('') + '/?' + query + '#' + fragment, url.fragment = fragment.join('');
let urlString = common.urlToString(url),
clean = jsdom(); clean = jsdom();
$('body').html('<div id="foo"></div>'); $('body').html('<div id="foo"></div>');
let e = $('#foo'); let e = $('#foo');
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x // special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x
if ( if (
query.slice(-1) === '&' && url.query[-1] === '&' &&
(parseInt(fragment.substring(0, 1), 10) >= 0 || fragment.charAt(0) === 'x' ) (parseInt(url.fragment.charAt(0), 10) >= 0 || url.fragment.charAt(0) === 'x')
) ) {
{ url.query.pop();
url = schema + '://' + address.join('') + '/?' + query.substring(0, query.length - 1); urlString = common.urlToString(url);
postfix = ''; postfix = '';
} }
e.text(prefix + url + postfix); e.text(prefix + urlString + postfix);
$.PrivateBin.Helper.urls2links(e); $.PrivateBin.Helper.urls2links(e);
let result = e.html(); let result = e.html();
clean(); clean();
url = $('<div />').text(url).html(); urlString = $('<div />').text(urlString).html();
return $('<div />').text(prefix).html() + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + $('<div />').text(postfix).html() === result; 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;
} }
); );
jsc.property( jsc.property(
@ -261,16 +259,16 @@ describe('Helper', function () {
this.timeout(30000); this.timeout(30000);
jsc.property( jsc.property(
'returns the URL without query & fragment', 'returns the URL without query & fragment',
jsc.elements(['http', 'https']), common.jscSchemas(false),
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.array(common.jscA2zString()), function (schema, url) {
jsc.array(common.jscQueryString()), url.schema = schema;
'string', const fullUrl = common.urlToString(url);
function (schema, address, path, query, fragment) { delete(url.query);
delete(url.fragment);
$.PrivateBin.Helper.reset(); $.PrivateBin.Helper.reset();
var path = path.join('') + (path.length > 0 ? '/' : ''), const expected = common.urlToString(url),
expected = schema + '://' + address.join('') + '/' + path, clean = jsdom('', {url: fullUrl}),
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
result = $.PrivateBin.Helper.baseUri(); result = $.PrivateBin.Helper.baseUri();
clean(); clean();
return expected === result; return expected === result;

View File

@ -80,23 +80,22 @@ describe('Model', function () {
jsc.property( jsc.property(
'returns the query string without separator, if any', 'returns the query string without separator, if any',
jsc.nearray(common.jscA2zString()), common.jscUrl(true, false),
jsc.nearray(common.jscA2zString()),
jsc.tuple(new Array(16).fill(common.jscHexString)), jsc.tuple(new Array(16).fill(common.jscHexString)),
jsc.array(common.jscQueryString()), jsc.array(common.jscQueryString()),
jsc.array(common.jscQueryString()), jsc.array(common.jscQueryString()),
'string', function (url, pasteId, queryStart, queryEnd) {
function (schema, address, pasteId, queryStart, queryEnd, fragment) { if (queryStart.length > 0) {
var pasteIdString = pasteId.join(''), queryStart.push('&');
queryStartString = queryStart.join('') + (queryStart.length > 0 ? '&' : ''), }
queryEndString = (queryEnd.length > 0 ? '&' : '') + queryEnd.join(''), if (queryEnd.length > 0) {
queryString = queryStartString + pasteIdString + queryEndString, queryEnd.unshift('&');
clean = jsdom('', { }
url: schema.join('') + '://' + address.join('') + url.query = queryStart.concat(pasteId, queryEnd);
'/?' + queryString + '#' + fragment const pasteIdString = pasteId.join(''),
}); clean = jsdom('', {url: common.urlToString(url)});
global.URL = require('jsdom-url').URL; global.URL = require('jsdom-url').URL;
var result = $.PrivateBin.Model.getPasteId(); const result = $.PrivateBin.Model.getPasteId();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
return pasteIdString === result; return pasteIdString === result;
@ -104,14 +103,9 @@ describe('Model', function () {
); );
jsc.property( jsc.property(
'throws exception on empty query string', 'throws exception on empty query string',
jsc.nearray(common.jscA2zString()), common.jscUrl(true, false),
jsc.nearray(common.jscA2zString()), function (url) {
'string', let clean = jsdom('', {url: common.urlToString(url)}),
function (schema, address, fragment) {
var clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/#' + fragment
}),
result = false; result = false;
global.URL = require('jsdom-url').URL; global.URL = require('jsdom-url').URL;
try { try {
@ -135,35 +129,24 @@ describe('Model', function () {
jsc.property( jsc.property(
'returns the fragment of a v1 URL', 'returns the fragment of a v1 URL',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()), function (url) {
jsc.array(common.jscQueryString()), url.fragment = common.btoa(url.fragment.padStart(32, '\u0000'));
'nestring', const clean = jsdom('', {url: common.urlToString(url)}),
function (schema, address, query, fragment) {
const fragmentString = common.btoa(fragment.padStart(32, '\u0000'));
let clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
return fragmentString === result; return url.fragment === result;
} }
); );
jsc.property( jsc.property(
'returns the v1 fragment stripped of trailing query parts', 'returns the v1 fragment stripped of trailing query parts',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()),
jsc.array(common.jscQueryString()),
'nestring',
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
function (schema, address, query, fragment, trail) { function (url, trail) {
const fragmentString = common.btoa(fragment.padStart(32, '\u0000')); const fragmentString = common.btoa(url.fragment.padStart(32, '\u0000'));
let clean = jsdom('', { url.fragment = fragmentString + '&' + trail.join('');
url: schema.join('') + '://' + address.join('') + '/?' + const clean = jsdom('', {url: common.urlToString(url)}),
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
@ -172,18 +155,12 @@ describe('Model', function () {
); );
jsc.property( jsc.property(
'returns the fragment of a v2 URL', 'returns the fragment of a v2 URL',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()), function (url) {
jsc.array(common.jscQueryString()),
'nestring',
function (schema, address, query, fragment) {
// base58 strips leading NULL bytes, so the string is padded with these if not found // base58 strips leading NULL bytes, so the string is padded with these if not found
fragment = fragment.padStart(32, '\u0000'); const fragment = url.fragment.padStart(32, '\u0000');
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment), url.fragment = $.PrivateBin.CryptTool.base58encode(fragment);
clean = jsdom('', { const clean = jsdom('', {url: common.urlToString(url)}),
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
@ -192,19 +169,13 @@ describe('Model', function () {
); );
jsc.property( jsc.property(
'returns the v2 fragment stripped of trailing query parts', 'returns the v2 fragment stripped of trailing query parts',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()),
jsc.array(common.jscQueryString()),
'nestring',
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
function (schema, address, query, fragment, trail) { function (url, trail) {
// base58 strips leading NULL bytes, so the string is padded with these if not found // base58 strips leading NULL bytes, so the string is padded with these if not found
fragment = fragment.padStart(32, '\u0000'); const fragment = url.fragment.padStart(32, '\u0000');
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment), url.fragment = $.PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join('');
clean = jsdom('', { const clean = jsdom('', {url: common.urlToString(url)}),
url: schema.join('') + '://' + address.join('') + '/?' +
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
@ -213,14 +184,9 @@ describe('Model', function () {
); );
jsc.property( jsc.property(
'throws exception on empty fragment of the URL', 'throws exception on empty fragment of the URL',
jsc.nearray(common.jscA2zString()), common.jscUrl(false),
jsc.nearray(common.jscA2zString()), function (url) {
jsc.array(common.jscQueryString()), let clean = jsdom('', {url: common.urlToString(url)}),
function (schema, address, query) {
var clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('')
}),
result = false; result = false;
try { try {
$.PrivateBin.Model.getPasteKey(); $.PrivateBin.Model.getPasteKey();

View File

@ -1,32 +1,39 @@
'use strict'; 'use strict';
var common = require('../common'); var common = require('../common');
function urlStrings(schema, longUrl, shortUrl) {
longUrl.schema = schema;
shortUrl.schema = schema;
let longUrlString = common.urlToString(longUrl),
shortUrlString = common.urlToString(shortUrl);
// ensure the two random URLs actually are sorted as expected
if (longUrlString.length <= shortUrlString.length) {
if (longUrlString.length === shortUrlString.length) {
longUrl.address.unshift('a');
longUrlString = common.urlToString(longUrl);
} else {
[longUrlString, shortUrlString] = [shortUrlString, longUrlString];
}
}
return [longUrlString, shortUrlString];
}
describe('PasteStatus', function () { describe('PasteStatus', function () {
describe('createPasteNotification', function () { describe('createPasteNotification', function () {
this.timeout(30000); this.timeout(30000);
jsc.property( jsc.property(
'creates a notification after a successfull paste upload', 'creates a notification after a successfull paste upload',
common.jscSchemas(), common.jscUrl(),
jsc.nearray(common.jscA2zString()), common.jscUrl(false),
jsc.array(common.jscQueryString()), function (url1, url2) {
'string', const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'),
common.jscSchemas(), expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a'),
jsc.nearray(common.jscA2zString()),
jsc.array(common.jscQueryString()),
function (
schema1, address1, query1, fragment1,
schema2, address2, query2
) {
var expected1 = schema1 + '://' + address1.join('') + '/?' +
encodeURI(query1.join('').replace(/^&+|&+$/gm,'') + '#' + fragment1),
expected2 = schema2 + '://' + address2.join('') + '/?' +
encodeURI(query2.join('').replace(/^&+|&+$/gm,'')),
clean = jsdom(); clean = jsdom();
$('body').html('<div><div id="deletelink"></div><div id="pastelink"></div></div>'); $('body').html('<div><div id="deletelink"></div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init(); $.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2); $.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
var result1 = $('#pasteurl')[0].href, const result1 = $('#pasteurl')[0].href,
result2 = $('#deletelink a')[0].href; result2 = $('#deletelink a')[0].href;
clean(); clean();
return result1 === expected1 && result2 === expected2; return result1 === expected1 && result2 === expected2;
@ -38,28 +45,26 @@ describe('PasteStatus', function () {
this.timeout(30000); this.timeout(30000);
jsc.property( jsc.property(
'extracts and updates URLs found in given response', 'extracts and updates IDN URLs found in given response',
jsc.elements(['http','https']), common.jscSchemas(false),
'nestring', 'nestring',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.array(common.jscQueryString()), function (schema, domain, url) {
jsc.array(common.jscAlnumString()),
'string',
function (schema, domain, tld, query, shortid, fragment) {
domain = domain.replace(/\P{Letter}|[\u00AA-\u00BA]/gu, '').toLowerCase(); domain = domain.replace(/\P{Letter}|[\u00AA-\u00BA]/gu, '').toLowerCase();
if (domain.length === 0) { if (domain.length === 0) {
domain = 'a'; domain = 'a';
} }
const expected = '.' + tld.join('') + '/' + (query.length > 0 ? url.schema = schema;
('?' + encodeURI(query.join('').replace(/^&+|&+$/gm,'')) + url.address.unshift('.');
shortid.join('')) : '') + (fragment.length > 0 ? url.address = domain.split('').concat(url.address);
('#' + encodeURI(fragment)) : ''), const urlString = common.urlToString(url),
expected = urlString.substring((schema + '://' + domain).length),
clean = jsdom(); clean = jsdom();
$('body').html('<div><div id="pastelink"></div></div>'); $('body').html('<div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init(); $.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', ''); $.PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(schema + '://' + domain + expected); $.PrivateBin.PasteStatus.extractUrl(urlString);
const result = $('#pasteurl')[0].href; const result = $('#pasteurl')[0].href;
clean(); clean();
@ -70,6 +75,102 @@ describe('PasteStatus', function () {
); );
} }
); );
// YOURLS API samples from: https://yourls.org/readme.html#API;apireturn
jsc.property(
'extracts and updates URLs found in YOURLS API JSON response',
common.jscSchemas(false),
common.jscUrl(),
common.jscUrl(false),
function (schema, longUrl, shortUrl) {
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
yourlsResponse = {
url: {
keyword: longUrl.address.join(''),
url: longUrlString,
title: "example title",
date: "2014-10-24 16:01:39",
ip: "127.0.0.1"
},
status: "success",
message: longUrlString + " added to database",
title: "example title",
shorturl: shortUrlString,
statusCode: 200
},
clean = jsdom();
$('body').html('<div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(JSON.stringify(yourlsResponse, undefined, 4));
const result = $('#pasteurl')[0].href;
clean();
return result === shortUrlString;
}
);
jsc.property(
'extracts and updates URLs found in YOURLS API XML response',
common.jscSchemas(false),
common.jscUrl(),
common.jscUrl(false),
function (schema, longUrl, shortUrl) {
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
yourlsResponse = '<result>\n' +
' <keyword>' + longUrl.address.join('') + '</keyword>\n' +
' <shorturl>' + shortUrlString + '</shorturl>\n' +
' <longurl>' + longUrlString + '</longurl>\n' +
' <message>success</message>\n' +
' <statusCode>200</statusCode>\n' +
'</result>',
clean = jsdom();
$('body').html('<div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse);
const result = $('#pasteurl')[0].href;
clean();
return result === shortUrlString;
}
);
jsc.property(
'extracts and updates URLs found in YOURLS proxy HTML response',
common.jscSchemas(false),
common.jscUrl(),
common.jscUrl(false),
function (schema, longUrl, shortUrl) {
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
yourlsResponse = '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'\t<head>\n' +
'\t\t<meta charset="utf-8" />\n' +
'\t\t<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads">\n' +
'\t\t<meta name="robots" content="noindex" />\n' +
'\t\t<meta name="google" content="notranslate">\n' +
'\t\t<title>PrivateBin</title>\n' +
'\t</head>\n' +
'\t<body>\n' +
'\t\t<p>Your paste is <a id="pasteurl" href="' + shortUrlString + '">' + shortUrlString + '</a> <span id="copyhint">(Hit [Ctrl]+[c] to copy)</span></p>\n' +
'\t</body>\n' +
'</html>',
clean = jsdom();
$('body').html('<div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse);
const result = $('#pasteurl')[0].href;
clean();
return result === shortUrlString;
}
);
}); });
describe('showRemainingTime', function () { describe('showRemainingTime', function () {
@ -79,18 +180,9 @@ describe('PasteStatus', function () {
'shows burn after reading message or remaining time v1', 'shows burn after reading message or remaining time v1',
'bool', 'bool',
'nat', 'nat',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()), function (burnafterreading, remainingTime, url) {
jsc.nearray(common.jscQueryString()), let clean = jsdom('', {url: common.urlToString(url)}),
'string',
function (
burnafterreading, remainingTime,
schema, address, query, fragment
) {
var clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragment
}),
result; result;
$('body').html('<div id="remainingtime" class="hidden"></div>'); $('body').html('<div id="remainingtime" class="hidden"></div>');
$.PrivateBin.PasteStatus.init(); $.PrivateBin.PasteStatus.init();
@ -117,18 +209,9 @@ describe('PasteStatus', function () {
'shows burn after reading message or remaining time v2', 'shows burn after reading message or remaining time v2',
'bool', 'bool',
'nat', 'nat',
jsc.nearray(common.jscA2zString()), common.jscUrl(),
jsc.nearray(common.jscA2zString()), function (burnafterreading, remainingTime, url) {
jsc.nearray(common.jscQueryString()), let clean = jsdom('', {url: common.urlToString(url)}),
'string',
function (
burnafterreading, remainingTime,
schema, address, query, fragment
) {
var clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragment
}),
result; result;
$('body').html('<div id="remainingtime" class="hidden"></div>'); $('body').html('<div id="remainingtime" class="hidden"></div>');
$.PrivateBin.PasteStatus.init(); $.PrivateBin.PasteStatus.init();

View File

@ -13,10 +13,9 @@ describe('UiHelper', function () {
jsc.property( jsc.property(
'redirects to home, when the state is null', 'redirects to home, when the state is null',
common.jscSchemas(), common.jscUrl(false, false),
jsc.nearray(common.jscA2zString()), function (url) {
function (schema, address) { const expected = common.urlToString(url),
var expected = schema + '://' + address.join('') + '/',
clean = jsdom('', {url: expected}); clean = jsdom('', {url: expected});
// make window.location.href writable // make window.location.href writable
@ -34,13 +33,11 @@ describe('UiHelper', function () {
jsc.property( jsc.property(
'does not redirect to home, when a new paste is created', 'does not redirect to home, when a new paste is created',
common.jscSchemas(), common.jscUrl(false),
jsc.nearray(common.jscA2zString()),
jsc.array(common.jscQueryString()),
jsc.nearray(common.jscBase64String()), jsc.nearray(common.jscBase64String()),
function (schema, address, query, fragment) { function (url, fragment) {
var expected = schema + '://' + address.join('') + '/?' + url.fragment = fragment.join('');
query.join('') + '#' + fragment.join(''), const expected = common.urlToString(url),
clean = jsdom('', {url: expected}); clean = jsdom('', {url: expected});
// make window.location.href writable // make window.location.href writable
@ -67,15 +64,12 @@ describe('UiHelper', function () {
jsc.property( jsc.property(
'redirects to home', 'redirects to home',
common.jscSchemas(), common.jscUrl(),
jsc.nearray(common.jscA2zString()), function (url) {
jsc.array(common.jscQueryString()), const clean = jsdom('', {url: common.urlToString(url)});
jsc.nearray(common.jscBase64String()), delete(url.query);
function (schema, address, query, fragment) { delete(url.fragment);
var expected = schema + '://' + address.join('') + '/', const expected = common.urlToString(url);
clean = jsdom('', {
url: expected + '?' + query.join('') + '#' + fragment.join('')
});
// make window.location.href writable // make window.location.href writable
Object.defineProperty(window.location, 'href', { Object.defineProperty(window.location, 'href', {

View File

@ -73,7 +73,7 @@ endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-zHSMNVafjkfbJWyExunMI9NvD/oSdifOiPCUGKdf4GHeCjl/IRi8RYyGoAzZPekqRDhhkl13SlfkUiQYKD0g5Q==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-TwHs7AU0ZWg8bylMgHXtPjnTeGeqSGHknoD3GENdI+xUjJYlup5vNNgL9pg3KSTgPnN49HqNt7h1NdqjHZ1Rfg==" crossorigin="anonymous"></script>
<!-- icon --> <!-- icon -->
<link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" /> <link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" />
<link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" />

View File

@ -51,7 +51,7 @@ endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-zHSMNVafjkfbJWyExunMI9NvD/oSdifOiPCUGKdf4GHeCjl/IRi8RYyGoAzZPekqRDhhkl13SlfkUiQYKD0g5Q==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-TwHs7AU0ZWg8bylMgHXtPjnTeGeqSGHknoD3GENdI+xUjJYlup5vNNgL9pg3KSTgPnN49HqNt7h1NdqjHZ1Rfg==" crossorigin="anonymous"></script>
<!-- icon --> <!-- icon -->
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" /> <link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" /> <link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />