Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions src/node/utils/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import path from 'node:path';
import {argv} from './Cli'
import jsonminify from 'jsonminify';
import log4js from 'log4js';
import {createHash} from 'node:crypto';
import randomString from './randomstring';
const suppressDisableMsg = ' -- To suppress these warning messages change ' +
'suppressErrorsInPadText to true in your settings.json\n';
Expand Down Expand Up @@ -1077,18 +1078,38 @@ export const reloadSettings = () => {
}

/*
* At each start, Etherpad generates a random string and appends it as query
* parameter to the URLs of the static assets, in order to force their reload.
* Subsequent requests will be cached, as long as the server is not reloaded.
* Etherpad appends this token as a ?v= query parameter on static assets
* and as the content seed for the padbootstrap-<hash>.min.js bundles, so
* clients invalidate their cache when a release goes out.
*
* For the rationale behind this choice, see
* https://github.com/ether/etherpad-lite/pull/3958
* Historically this was `randomString(4)`, regenerated on every boot. That
* broke horizontally-scaled deployments (multi-pod behind an ingress):
* every pod hashed the bootstrap bundle with its own seed, so an HTML
* response from pod A referenced `padbootstrap-ABCD.min.js` while pod B
* only served `padbootstrap-WXYZ.min.js`, producing 404s on any cross-pod
* request (issue #7213).
*
* ACHTUNG: this may prevent caching HTTP proxies to work
* TODO: remove the "?v=randomstring" parameter, and replace with hashed filenames instead
* Derive the token deterministically from the Etherpad version and
* whatever git SHA is available. Pods that ship the same artifact now
* produce the same hash, and the token still rotates per release so
* caches invalidate correctly.
*
* Precedence: ETHERPAD_VERSION_STRING env var (explicit integrator
* override) > sha256(version + "|" + gitVersion) > package.json version.
*
* For the original cache-busting rationale, see PR #3958.
*/
settings.randomVersionString = randomString(4);
logger.info(`Random string used for versioning assets: ${settings.randomVersionString}`);
const explicit = process.env.ETHERPAD_VERSION_STRING;
if (explicit) {
settings.randomVersionString = explicit;
} else {
const pkgVersion = require('../../package.json').version as string;
settings.randomVersionString = createHash('sha256')
.update(`${pkgVersion}|${settings.gitVersion || ''}`)
.digest('hex')
.slice(0, 8);
}
logger.info(`String used for versioning assets: ${settings.randomVersionString}`);
Comment on lines +1102 to +1112
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. 4-space indent in reloadsettings() 📘 Rule violation ⚙ Maintainability

Newly added code in reloadSettings() uses 4+ space indentation (and deeper levels), which violates
the requirement to indent with 2 spaces only. This reduces formatting consistency and can cause
style/lint failures in environments enforcing the rule.
Agent Prompt
## Issue description
Newly added code uses indentation other than 2 spaces, violating the repo compliance formatting rule.

## Issue Context
The `randomVersionString` derivation block added in `reloadSettings()` is indented with 4+ spaces.

## Fix Focus Areas
- src/node/utils/Settings.ts[1102-1112]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

};

export const exportedForTestingOnly = {
Expand Down
39 changes: 39 additions & 0 deletions src/tests/backend/specs/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,43 @@ describe(__filename, function () {
}
});
});

// Regression test for https://github.com/ether/etherpad/issues/7213.
// Pre-fix: randomVersionString was `randomString(4)`, regenerated on every
// boot — the padbootstrap-<hash>.min.js filename therefore differed across
// pods of the same build, producing 404s on any cross-pod request in a
// horizontally-scaled deployment. Post-fix: the token is a deterministic
// hash of version + gitVersion (or an explicit
// ETHERPAD_VERSION_STRING env var).
describe('randomVersionString determinism (issue #7213)', function () {
it('is a stable 8-hex-char sha256 prefix by default', function () {
const settings = require('../../../node/utils/Settings');
assert.match(settings.randomVersionString, /^[0-9a-f]{8}$/,
`expected 8-char hex, got ${settings.randomVersionString}`);
});

it('honours ETHERPAD_VERSION_STRING as an explicit override', function () {
const settingsMod = require('../../../node/utils/Settings');
const original = process.env.ETHERPAD_VERSION_STRING;
const savedSettingsFile = settingsMod.settingsFilename;
const savedCredsFile = settingsMod.credentialsFilename;
const savedToken = settingsMod.randomVersionString;
process.env.ETHERPAD_VERSION_STRING = 'integrator-1';
settingsMod.settingsFilename = path.join(__dirname, 'settings.json');
settingsMod.credentialsFilename = path.join(__dirname, 'credentials.json');
try {
// The token is set by reloadSettings, not by parseSettings alone.
// Re-run the full reload path so the env var is consulted.
settingsMod.reloadSettings();
assert.strictEqual(settingsMod.randomVersionString, 'integrator-1',
'ETHERPAD_VERSION_STRING should be used verbatim');
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
} finally {
if (original == null) delete process.env.ETHERPAD_VERSION_STRING;
else process.env.ETHERPAD_VERSION_STRING = original;
settingsMod.settingsFilename = savedSettingsFile;
settingsMod.credentialsFilename = savedCredsFile;
settingsMod.randomVersionString = savedToken;
}
});
});
});
Loading