diff --git a/apps/server/src/core/share/inject-tracker-head.util.spec.ts b/apps/server/src/core/share/inject-tracker-head.util.spec.ts
new file mode 100644
index 00000000..8bf32eb9
--- /dev/null
+++ b/apps/server/src/core/share/inject-tracker-head.util.spec.ts
@@ -0,0 +1,60 @@
+import { injectTrackerHead } from './inject-tracker-head.util';
+
+// Pins the public-share trackerHead injection invariant (ShareSeoController).
+// The admin snippet is trusted content and MUST land byte-for-byte before the
+// first . The critical regression these tests guard is the function vs
+// string replacer: a string replacement interprets `$&`/`$$`/`` $` ``/`$'`
+// inside the snippet as substitution patterns and mangles the tracker. The
+// byte-for-byte test below FAILS on the old string-replacer implementation and
+// passes only with the function replacer.
+
+const HTML = '
tb';
+
+describe('injectTrackerHead', () => {
+ it('inserts the snippet immediately before the first ', () => {
+ const out = injectTrackerHead(HTML, '');
+ expect(out).toBe(
+ 't\nb',
+ );
+ });
+
+ it('inserts a snippet containing $& byte-for-byte (function replacer)', () => {
+ const snippet = '';
+ const out = injectTrackerHead(HTML, snippet);
+ expect(out).toContain(`${snippet}\n`);
+ // The literal "$&" survives; a string replacer would have spliced in the
+ // matched "" here.
+ expect(out).toContain('$&');
+ expect(out).not.toContain('"');
+ });
+
+ it('inserts a snippet containing $$, $` and $\' byte-for-byte', () => {
+ // All four special replacement patterns in one snippet.
+ const snippet = "";
+ const out = injectTrackerHead(HTML, snippet);
+ expect(out).toContain(`${snippet}\n`);
+ });
+
+ it('returns html unchanged for an empty trackerHead', () => {
+ expect(injectTrackerHead(HTML, '')).toBe(HTML);
+ });
+
+ it('returns html unchanged for a whitespace-only trackerHead', () => {
+ expect(injectTrackerHead(HTML, ' \n\t ')).toBe(HTML);
+ });
+
+ it('returns html unchanged for an undefined trackerHead', () => {
+ expect(injectTrackerHead(HTML, undefined)).toBe(HTML);
+ });
+
+ it('returns html unchanged when there is no marker', () => {
+ const noHead = 'no head here';
+ expect(injectTrackerHead(noHead, '')).toBe(noHead);
+ });
+
+ it('injects before only the FIRST when several exist', () => {
+ const twoHeads = '';
+ const out = injectTrackerHead(twoHeads, 'X');
+ expect(out).toBe('X\n');
+ });
+});
diff --git a/apps/server/src/core/share/inject-tracker-head.util.ts b/apps/server/src/core/share/inject-tracker-head.util.ts
new file mode 100644
index 00000000..58828ef8
--- /dev/null
+++ b/apps/server/src/core/share/inject-tracker-head.util.ts
@@ -0,0 +1,30 @@
+/**
+ * Injects an admin-authored analytics/tracker snippet verbatim into the
+ * of a public-share page.
+ *
+ * `trackerHead` is admin-only trusted content (writable only via the
+ * admin-gated workspace settings) and must be inserted BYTE-FOR-BYTE before the
+ * first `` marker. A plain string replacement would interpret `$&`,
+ * `$$`, `` $` `` and `$'` inside the snippet as substitution patterns and mangle
+ * the tracker, so a FUNCTION replacer is used: its return value is inserted
+ * literally with no special-pattern interpretation.
+ *
+ * The snippet is deliberately NOT escaped (it is trusted HTML/JS). Returns the
+ * html unchanged when:
+ * - trackerHead is undefined / empty / whitespace-only, or
+ * - there is no `` marker to anchor the injection.
+ */
+export function injectTrackerHead(
+ html: string,
+ trackerHead: string | undefined,
+): string {
+ if (typeof trackerHead !== 'string' || trackerHead.trim().length === 0) {
+ return html;
+ }
+ if (!html.includes('')) {
+ return html;
+ }
+ // Function replacer: the return value is inserted literally, so `$&`/`$$`/
+ // `` $` ``/`$'` in the admin snippet are NOT treated as substitution patterns.
+ return html.replace('', () => `${trackerHead}\n`);
+}
diff --git a/apps/server/src/core/share/share-seo.controller.ts b/apps/server/src/core/share/share-seo.controller.ts
index 1c443dcc..db20ef0a 100644
--- a/apps/server/src/core/share/share-seo.controller.ts
+++ b/apps/server/src/core/share/share-seo.controller.ts
@@ -8,6 +8,7 @@ import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
import { EnvironmentService } from '../../integrations/environment/environment.service';
import { Workspace } from '@docmost/db/types/entity.types';
import { htmlEscape } from '../../common/helpers/html-escaper';
+import { injectTrackerHead } from './inject-tracker-head.util';
@Controller('share')
export class ShareSeoController {
@@ -97,21 +98,19 @@ export class ShareSeoController {
// pages only. It is trusted content, so it is NOT escaped. The htmlEmbed
// block itself is sandboxed and is the safe surface for everyone else.
const trackerHead = (workspace?.settings as any)?.trackerHead;
- if (typeof trackerHead === 'string' && trackerHead.trim().length > 0) {
- if (transformedHtml.includes('')) {
- // Function replacer: the snippet is admin-authored trusted content and
- // must be injected verbatim. A string replacement would interpret `$&`,
- // `$'`, `` $` `` and `$$` inside it as substitution patterns and mangle
- // the tracker; a function return value is inserted literally.
- transformedHtml = transformedHtml.replace(
- '',
- () => `${trackerHead}\n`,
- );
- } else {
- this.logger.warn(
- 'trackerHead is configured but no marker was found in the share index HTML; tracker snippet was not injected.',
- );
- }
+ const beforeInjection = transformedHtml;
+ transformedHtml = injectTrackerHead(transformedHtml, trackerHead);
+ if (
+ beforeInjection === transformedHtml &&
+ typeof trackerHead === 'string' &&
+ trackerHead.trim().length > 0
+ ) {
+ // A non-empty snippet was configured but nothing was injected: the only
+ // reason injectTrackerHead leaves the html unchanged for a non-empty
+ // snippet is a missing marker.
+ this.logger.warn(
+ 'trackerHead is configured but no marker was found in the share index HTML; tracker snippet was not injected.',
+ );
}
res.type('text/html').send(transformedHtml);