Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only · Issue #13405 · objectstack-ai/objectstack · GitHub
Skip to content

[security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405

Description

@baozhoutao

Existence (reproduction withheld pending maintainer)

A datasource credential placed in a nested config position (not the top-level credential key) is accepted at the runtime admin door and then served in cleartext through the datasource read doors (GET /api/v1/datasources/:name and GET /api/v1/meta/datasource), with hasSecret:false / redactedConfigKeys:[]. The equivalent credential at the top-level key is correctly refused at publish and redacted on read — so the redactor is top-level-key-only and does not descend into nested config objects. Observed across all three replicas of a live multi-node deployment; the meta-registry entry propagates cluster-wide at create time.

Per the maintainer disclosure ruling (2026-08-18, #9387) and RUNNER protocol rule 2's carve-out, the reproduction, the exact nested position, and the request bodies are NOT published here or anywhere on GitHub. They are held in the QA runner session and available to the maintainer directly. Existence published, recipe withheld, is a complete and actionable report.

Why this is distinct from prior art

Verification

Independently reproduced twice by two separate QA agents (original run + a dedicated rule-7 verify-pass), each from its own fixtures, with the top-level position as the negative control (correctly refused). No cross-tenant read was involved — the exposure is on the authoring tenant's own read path. All planted secrets were scrubbed after each run (verified absent from every door on every replica).

Suggested direction

Make the read-side redactor (and the publish-time inline-credential refusal) recurse into nested config objects, so the nested position is treated identically to the top-level key it mirrors. A second-order cleanup gap was also seen: datasource DELETE clears the admin/detail-door registry but does not evict the /api/v1/meta/datasource in-memory metadata registry cluster-wide (create broadcasts, delete does not mirror), which prolonged the exposure until the entry was scrubbed.

QA-source: #13404 · integration-system.datasource-credential-refusal-matrix · clauses 1,3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions