[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[0.2] Backport #4715 - #4724

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3
Jun 19, 2026
Merged

[0.2] Backport #4715#4724
TheBlueMatt merged 2 commits into
lightningdevkit:0.2from
TheBlueMatt:2026-06-0.2.3

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Missed #4715 but caught it before we uploaded anything, so sneak this in.

tnulland others added 2 commits June 19, 2026 01:38
`LSPSDateTime::is_past` coerced `chrono`'s `i64` timestamp into a
`u64` via `try_into().expect(...)`. Because `LSPSDateTime` is parsed
from peer-controlled RFC 3339 strings (which can be pre-1970 and so
yield negative timestamps), this could be triggered remotely: an
attacker-supplied `valid_until` / `expires_at` field of e.g.
`"1900-01-01T00:00:00Z"` would parse successfully, land in LSPS state
before any HMAC / promise check, and panic the LSP thread on the next
`prune_pending_requests` sweep. Concretely reachable today via LSPS2
`opening_fee_params.valid_until` (in the buy request) and the LSPS1
expiry fields.
Make `LSPSDateTime::from_str` reject pre-epoch datetimes, and route
serde deserialization through it: `#[serde(transparent)]` was
delegating Deserialize directly to `chrono`'s impl and bypassing our
parser, so peer JSON had to be guarded separately. With both paths
funnelled through one parser, no `LSPSDateTime` value with a negative
inner timestamp can be constructed and `is_past` is safe by
construction.
Co-Authored-By: HAL 9000
Backport of 837763a
Conflicts resolved in:
* lightning-liquidity/src/lsps0/ser.rs
@ldk-reviews-bot

ldk-reviews-bot commented Jun 19, 2026

Copy link
Copy Markdown

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

This is a clean, correct backport of a security fix. The change rejects pre-epoch datetimes at parse time (in from_str), and the new custom Deserialize impl routes JSON deserialization through from_str so peer messages get the same validation. The imports (de, Deserializer, Deserialize) are all present, serialization (RFC3339 string) and deserialization remain consistent, and Readable also uses from_str so all parse paths are covered. The CHANGELOG entry is appropriate.

No issues found.

@TheBlueMatt
TheBlueMatt merged commit bc34ee5 into lightningdevkit:0.2Jun 19, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@TheBlueMatt@ldk-reviews-bot@ldk-claude-review-bot@jkczyz@benthecarman@tnull