lovell

Stars and mentions

Last 30 days, Oct 4 – Nov 2

GitHub stars0
mentions5
01234Oct 4Oct 18Nov 2
UTC days · repositories lovell owns0 repositories

Package downloads

No package download history available yet.

Top repositories

lovell’s repositories ranked by pull request authors, as on trending.

Loading

Contributes to

Repositories outside lovell’s account where lovell opened or reviewed pull requests in the past 365 days.

Who noticed lovell

Everyone who starred or forked lovell’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

5posts
Nov 30 posts
  1. I Can't Reproduce 1,190 of My 1,386 Covers on This Machine There is a sentence at the top of my cover generator that I wrote myself. It sits there without Turkish characters, the way the code has it: *"Existing PNG idempotent overwrite — same input, same output (sharp is deterministic)."* That line has been there since 7 May 2026, the generator's first commit. I have trusted it for six months. Tonight I tested it for the first time. I re-rendered the covers of all 1,386 Turkish articles in the archive with today's generator and compared before and after, byte for byte. 1,190 of them didn't match. So I cannot reproduce 86 percent of my site's covers today by running the same code against the same titles. Chasing it turned up two causes. One was a decision I made deliberately five months ago; the other I had never noticed until tonight. Thirteen covers fit neither, and I leave them unsolved below. Images I never look at Let me confess something first: I don't see these covers when I visit my own site. Post cards don't render the cover — there's even an explicit note inside the component saying so. The cover image only goes into the og:image and twitter:image tags, into feed.json and the RSS output, and into the LinkedIn and Bluesky sharing endpoints. So these 2,772 PNG files (Turkish and English, two per article) are my blog's face — but only in the places I never look. The little box that appears when someone shares my link on WhatsApp, the preview in Slack, the card on X. I have never wondered how my own house looks from the street. That's why generating covers has always been a formality for me. When I write a new article I run two commands, glance at the resulting PNG once — did the title overflow, are the Turkish letters right — and commit it. That single glance is enough to validate one cover; it is not enough to validate that 1,386 covers resemble each other, because I never put two of them side by side. The measurement: hash, re-render, hash The method is boring on purpose. I took the SHA-256 digest of every cover, ran the generator with --all to re-render them all, took the digests again, and compared the two lists. If the generator is deterministic the two lists must be identical. My first reaction was "so sharp isn't deterministic after all." Wrong reaction. When I render the same cover twice in a row the bytes are identical; determinism holds. What broke is the stability of the inputs. Drawing that distinction mattered, because the measurement itself can mislead you. The cover generator touches the articles' frontmatter while it runs — it normalises the coverImageAlt field — so an --all run rewrites not just 2,772 PNGs but over a thousand .mdx files too. I reverted all of them with git afterwards. Had I run this experiment in a dirty working tree, I could not have told my measurement apart from my own leftovers. Sorting the changed files by publication date revealed a clean break: | Publication month | Not reproducible | Byte-identical | |---|---:|---:| | 2024-05 … 2026-03 (four months) | 32 | 0 | | 2026-04 | 368 | 0 | | 2026-05 | 274 | 0 | | 2026-06 | 228 | 3 | | 2026-07 | 199 | 1 | | 2026-08 | 78 | 19 | | 2026-09 | 11 | 85 | | 2026-10 | 0 | 86 | | 2026-11 | 0 | 1 | | Total | 1,190 | 195 | The rows add up to 1,385; the 1,386th article's cover is not on disk, and I'll come back to it. All 86 articles I published in October match. Not one of the 368 I published in April does. The break is at the end of August. First cause: two different machines print my covers Git does not forget who added what — but it depends which question you ask. My first reflex was --diff-filter=A, which gives each cover's first adder: 833 for the bot, 553 for me. Then it struck me that the bytes sitting in the repo today may not have been written by that commit. Ask for the last writer and the numbers shift: first adder (--diff-filter=A): BOT 833 | ME 553 last writer (--diff-filter=AM): BOT 712 | ME 674 The 121 covers in between were added by the bot and later overwritten by me. The second question is the right one, because what I'm trying to reproduce is today's bytes. Crossing the last writer with the design era and the outcome clears things up: | Last writer | Design | Outcome | Count | |---|---|---|---:| | Me | dark (current) | byte-identical | 195 | | Me | dark (current) | doesn't match | 13 | | Me | coloured (retired) | doesn't match | 466 | | Bot | dark (current) | doesn't match | 399 | | Bot | coloured (retired) | doesn't match | 312 | | Bot | coloured (retired) | byte-identical | 1 | Of the 712 covers the bot wrote last, 711 cannot be reproduced on my machine. The one that does match is interesting in its own right: no article points at it, so the generator never opens it. More on that shortly. Three quarters of my font stack is decoration Here are the relevant lines from my generator: // librsvg uses fontconfig; a font family that supports Turkish // characters (ş, ğ, ı, ç, ü, ö) cross-platform: // 1) Inter — Linux (apt: fonts-inter) // 2) Noto Sans — Linux fallback (fonts-noto-core) // 3) Helvetica — macOS native, supports Turkish // 4) DejaVu Sans — Linux ultimate fallback const FONT_FAMILY = "Inter, 'Noto Sans', Helvetica, 'DejaVu Sans', sans-serif"; The intent is clear: Inter on Linux, Helvetica on macOS. That is also what CSS promises. CSS Fonts Module Level 4 calls font-family "a prioritized list" and says the user agent "iterates through the list of family names until it matches an available font that contains a glyph for the character to be rendered." It even states the reason: because available fonts differ across platforms. I checked whether the list really is walked in order. Same text, same SVG template, changing only the font-family value: Verdana (reference) 4dace8fe4ffe Helvetica (reference) c1ac04a63934 ZzQq1, ZzQq2, Verdana 4dace8fe4ffe <- Verdana won Inter, 'Noto Sans', Verdana 4dace8fe4ffe <- Verdana won THE REAL STACK c1ac04a63934 <- Helvetica won The list is walked in order, exactly as the spec says: it skips the two made-up names in front and settles on Verdana. In my stack Inter and Noto Sans are skipped, and the winner is Helvetica, in third place. Apple's macOS font list supports this: Helvetica sits under "Installed or downloadable fonts," while plain Noto Sans is absent from the list entirely — only script-specific siblings like Noto Sans Kannada and Noto Sans Myanmar are there. DejaVu Sans isn't on this machine either. Inter is here, but in a place that can't help: inside the font folder of the Java runtime bundled with IntelliJ IDEA and Android Studio. It isn't in a system font directory, so the rendering engine never sees it. Let me be precise about what this covers. On this Mac only one of the four names resolves. What happens on the CI machine I did not measure; all I have is its output. That machine is my own infrastructure and fc-list is a single command — not looking was a choice, made because I wanted to close the measurement notebook tonight. How different are the letters? I started with a single cover and nearly wrote down bad evidence: the title line on the bot's cover is 763 pixels, mine 638. That is n=1, and worse, it compares an inked width against an advance sum computed from the font file — two different quantities, with side bearings and letter-spacing in between. If they match, it's a coincidence. So I did a paired measurement instead. I picked 60 bot-rendered covers at random from those carrying the current design, measured the inked width of the widest line in each title block, then re-rendered the same cover here and measured again: 60 covers, paired ratio (bot / mine): 48 covers -> median 1.187 (p25 1.165 p75 1.190) 12 covers -> ~1.00 In 48 covers the bot's line is about 19 percent wider than mine, the spread is narrow, and a gap that size can only come from the letters themselves. In the remaining 12 the ratio sits at one. I opened one of those and looked: the letters are the same width, yet the file digest differs. So "the bot's covers are in a different typeface" is true for the majority, not for all — and I could not isolate why that minority fails. So why did nobody complain? Pango does not actually stay silent when a font can't be loaded. First I had to establish which engine was even talking. My comment says "librsvg uses fontconfig"; running the same renders with the PANGOCAIRO_BACKEND variable settled it: default = PANGOCAIRO_BACKEND=coretext (hashes IDENTICAL) PANGOCAIRO_BACKEND=fc (every hash CHANGED) What's in play is not fontconfig but CoreText. My own comment misdescribes my own code. I once wrote an article about my recipes misdescribing my code; while writing it I had no idea the same mistake was sitting under this line. The irony is that the fc backend recognises more of the names in my stack — the engine my code wrongly claims would have behaved better than the one actually running. I went to the source where the warning is emitted (sharp ships version 1.57.0; the function I quote is unchanged on main). pango_core_text_font_map_load_fontset first tries to load the font set with the requested description. Failing that, it swaps the family name for Sans and tries again with the same style. Failing that too, it resets variant, weight and width to normal — printing the warning before that third attempt — and tries one last time. The warning's flag is a static boolean, so it fires once per process. !Diagram What the diagram says is this: the warning only fires when the list fails end to end. My list resolves to something on both machines — to Helvetica on one, to something else on the other. Both count as success. That is why there isn't a single line of warning in my terminal or in the CI log. The silence isn't a malfunction; it is the design. To see the warning fire, I triggered it on purpose. I picked Impact, whose file is on my disk and which is on Apple's list: Impact (no stretch given) fd8b295f25f5 <- fell to the default Impact font-stretch="condensed" 341c125b00c1 <- actually Impact Impact font-stretch="semi-condensed" -> Pango-WARNING: couldn't load font "Impact Semi-Condensed Not-Rotated 58", modified variant/weight/stretch as fallback, expect ugly output. The reason is in the font file: Impact's usWidthClass is 3, that is, "condensed." Pango's CoreText side labels that face PANGO_STRETCH_CONDENSED and produces no other width step. The default request is normal width, so the match falls through; the moment I write condensed the font arrives; when I ask for semi-condensed nothing is left and the warning appears. A font on my disk, listed by Apple, does not arrive when called by name — because of one metric. Two more things that fell out while measuring First: the generate-hero.ts --all command announces "Total posts: 2772," meaning it counts English articles as separate posts too. For every .en.mdx file it renders a cover named <slug>.en.png — 1,386 files nothing points at. The real name for English covers is -en.png, with a hyphen, not a dot. Not one file with a .en.png extension exists anywhere in the repository's history, so that flag has never turned into a commit; had it done so, those files would have settled in quietly. Second, that 1,386th cover I kept postponing. Exactly one coverImage has no file on disk, and the symmetry bothered me: exactly one PNG is pointed at by no article. One missing, one spare. The missing one's story is the part that stung. An article I published on 9 June 2026 had a cover; my own cleanup commit deleted it on 9 July. The message reads: *"fix(content-gen): close the orphan fallback cover PNG leak + clean up 173 existing orphans."* The command that swept up orphan covers took a referenced cover along with them. Its English twin came back the next day; the Turkish one did not. I verified the result on the live site: page: 200 og:image = .../bootstrap-deadlock-cluster-domain-controller.png image HTTP: 404 For four months, anyone who shared that article's link saw a blank space where the picture should be. The page is perfectly healthy; the only broken part is the side I never look at. Second cause: a decision I made five months ago Not all 1,190 failing covers are about typefaces. 778 of them are visibly different at a glance: taking a 40×40 pixel square from the bottom-left corner of each cover and reading its average colour gives 779 covers in the colour scheme I retired and 607 in today's dark one. (Shift the sampling window and the number moves to 777/609; the boundary is sharp, but where the square sits matters for a handful of covers.) The commit message from 9 June 2026 reads: *"feat(covers): new dark + neon-blue cover design (all new posts)."* Those three words in parentheses, "all new posts," were a deliberate decision that day. Re-rendering the old ones would take a while, the covers weren't visible on the site anyway, so I left them. Five months later the majority of my archive still carries that retired design. Every shared link to my blog shows one of two different brands, depending on when it was written. I can't say "I didn't notice"; it was my decision. What I didn't notice is how long that decision would live. What I made that day was really a deferral, and deferrals have a bad habit: they don't make it onto a calendar. The two causes also conceal each other. Because the old covers look different to the eye, the answer to "why don't they match" seemed ready-made. Had I looked at them and said "right, design change," I would never have opened the 399 bot covers carrying the same design as today's. The thirteen left open And then the row in the table that occupies me most: 13 covers carrying today's design, last written by me, that still don't match. Same machine, same design, same stack. Neither of my causes explains them. There is an alternative I should put on the table honestly: the renderer version. package.json pins sharp at 0.35.5 today; what is installed on this Mac is 0.34.5. The pinning history isn't flat either — until 11 August 2026 it was a range like ^0.34.5, then 0.35.3 that day, then 0.35.5 on 28 September. So the machines may differ not only in fonts but in drawing libraries. That does not explain the 19 percent width gap in the 60-cover measurement; that gap comes from the advance width of letters, and a library upgrade does not move it. But it could very well explain those 13 covers, and the 12 out of 60 whose ratio sits at one. I didn't measure it. A number with "I don't know" behind it beats a number with the wrong reason behind it. Three minutes' worth of checks for your own setup Is your stack picking anything other than the default? Render the same text once more with a font name that doesn't exist. If the outputs match, your stack lands on the engine's default — which is not the same as "it does nothing," because the default may itself be a name from your list. On this Mac Times behaves exactly like that: installed on disk, on Apple's list, yet its output is identical to a made-up name's. To sharpen the diagnosis, add a name that does diverge, like Verdana, as a positive control. How many machines wrote the bytes in your repo? --diff-filter=A gives you the first adder, which isn't what you want. Use `git log --diff-filter=AM --name-only` for the last writer and count the commit authors. For me the two questions differed by 121 covers. Does the archive match what today's generator produces? Hash, re-render, hash, compare. Those three steps took ten minutes and demolished a six-month-old assumption. Just make sure your working tree is clean first. What I'm going to do Vendor the font into the repo. That was my first reflex: put a .ttf file in the repository and load it explicitly when rendering. sharp's text API has a fontfile parameter, and I tried it — on this setup it has no effect at all. The output is byte-identical with and without the file, and *"Fontconfig error: Cannot load default config file"* lands on top. The parameter writes into fontconfig's application font set, while the engine in play is CoreText. So the cost isn't "a few hundred kilobytes": I would have to pin the backend and a fontconfig configuration as well, or abandon the SVG template for a different drawing path. I learned the price just now. Render covers on one machine only. I was about to dismiss this with "the pipeline would publish without covers while the Mac is off," then I looked at the workflow: runs-on: [self-hosted, itwise-mac]. The pipeline is already tied to this machine. My objection was void. Put in a regression gate. This is what was really missing. The whole article says "no warning, no test told me"; so I should write the thing that tells me. A CI job that re-renders twenty random covers and compares digests is enough — and tonight's measurement is its prototype. I'll do all three, but not with this article. All I did today was measure; re-rendering the whole archive would change 2,772 files in a single commit. I'm writing the open items here so I can't duck them: a regression gate, measuring how the font can actually be pinned, renewing the covers still on the retired design, and re-rendering that one deleted cover. Meanwhile the --all flag looks innocent, but in this repo it is a command that silently rewrites 2,772 files. I reverted everything while measuring; even so, I won't run it again without checking git status first. What I take from it Something similar happened when I sat down to fix my alt text: the gap between what the code promises and what the page produces can stay open a long time, because nobody looks. There the missing thing was text; here it is the typeface. But the real lesson of tonight is different. I was awarding the adjective "deterministic" to my code; that adjective doesn't belong to code. The same code, with unpinned inputs, is deterministic only on the same machine — and "the same machine" covers the installed fonts, the drawing library's version, and which backend happens to be in play. My cover generator is deterministic; on two separate machines, with two separate results. Calling something reproducible requires having run it twice. And not both times in the same place. Official Sources - CSS Fonts Module Level 4 — W3C - Fonts included with macOS Tahoe — Apple Support - Pango — pangocoretext-fontmap.c (CoreText font map) - sharp — high performance Node.js image processing

    2 reactions
  2. YouTube · · YouTube description · 투더제이 TTJ | AI·코딩 수익화 · lovell/sharp

    AI 모션그래픽 저장소 20개 모음 모션그래픽 제작에 쓰는 저장소 20개를 기획·에셋·움직임·입체 장면·출력 순서로 정리했습니다. 필요한 도구만 조합하고 각 저장소의 설치 안내와 이용 조건을 확인하면 됩니다. 도구·자료 주소 penpot/penpot excalidraw/excalidraw fontsource/fontsource lucide-icons/lucide svg/svgo lovell/sharp airbnb/lottie-web rive-app/rive-wasm motiondivision/motion greensock/GSAP juliangarnier/anime theatre-js/theatre mrdoob/three.js pmndrs/react-three-fiber pmndrs/drei pmndrs/postprocessing remotion-dev/remotion remotion-dev/skills FFmpeg/FFmpeg Vanilagy/mediabunny #모션그래픽 #개발도구 #AI활용 #투더제이 [en-US] 20 AI Motion Graphics Resources Collection We have organized 20 repositories used for motion graphics production in the order of Planning, Assets, Motion, 3D Scenes, and Output. Simply combine the tools you need and check the installation instructions and terms of use for each repository. Tool/Resource Addresses penpot/penpot excalidraw/excalidraw fontsource/fontsource lucide-icons/lucide svg/svgo lovell/sharp airbnb/lottie-web rive-app/rive-wasm motiondivision/motion greensock/GSAP gi thub.com/juliangarnier/anime theatre-js/theatre mrdoob/three.js pmndrs/react-three-fiber pmndrs/drei pmndrs/postprocessing remotion-dev/remotion remotion-dev/skills FFmpeg/FFmpeg Vanilagy/mediabunny #MotionGraphics #DevelopmentTools #AIUtilization #ToTheJ

    81 likes · 2 comments · 2,503 views
  3. Next.js Security Update October 2026: Patch the use cache RCE, SVG sharp RCE and Server Actions DoS The Next.js security update of October 14, 2026 fixes three vulnerabilities: two critical unauthenticated remote code execution (RCE) bugs, one in use cache and one in SVG image optimization through sharp, and a high severity denial of service in Server Actions. Upgrade to Next.js 16.4.1 or later on 16.x, or 15.5.28 on 15.x, make sure sharp is 0.35.5 or later, then rebuild and redeploy. The patch is easy to apply and easy to get wrong when you self-host. I reproduced the upgrade on real installs and found that upgrading Next.js 15 to 15.5.28 left the vulnerable sharp 0.34.5 in place, while npm audit on an unpatched next@16.4.0 reported 0 vulnerabilities. This guide shows how to check whether you are affected, a script that checks for you, and the exact steps to patch a Docker or Coolify deployment. Everything is cited to the official advisories. What did the October 2026 Next.js security update fix? It fixed three advisories published on October 14, 2026, all patched in Next.js 16.4.1 (Active LTS) and 15.5.28 (Maintenance LTS), according to the official Next.js security update. Two allow unauthenticated remote code execution with a CVSS 4.0 score of 9.5; the third lets crafted requests pin a server's CPU. | Advisory | Severity | What it is | Affected next versions | Fixed in | Workaround | | --- | --- | --- | --- | --- | --- | | GHSA-673q-mjm6-r5xr | Critical, 9.5 | RCE in use cache functions that return multiple typed arrays | 16.0.0 to 16.4.0 | 16.4.1 | None | | CVE-2026-96889 / GHSA-2464-fhr4-w9xw | Critical, 9.5 | RCE in the Image Optimization API with SVG, via sharp's librsvg, on glibc Linux | 10.0.0 to 15.5.27, 16.0.0 to 16.4.0 | 15.5.28, 16.4.1 | sharp 0.35.5 or later | | GHSA-p3m5-59mp-jv5r | High, 8.2 | DoS: crafted requests cause excessive CPU in App Router apps with Server Actions | 13.0.0 to 15.5.27, 16.0.0 to 16.4.0 | 15.5.28, 16.4.1 | None | The two React Server Components bugs also have upstream React advisories: GHSA-xqc2-xf4c-x3h3 for the typed-array RCE and GHSA-h3gv-c5rc-p453 for the DoS. Next.js ships its own compiled copy of react-server-dom-webpack and react-server-dom-turbopack inside the next package (I checked node_modules/next/dist/compiled), so upgrading next is what fixes a Next.js app, not bumping react alone. Quick definitions | Term | What it means | | --- | --- | | RCE | Remote code execution: an attacker can run code on your server | | DoS | Denial of service: the server stops answering legitimate requests | | GHSA | A GitHub Security Advisory ID | | sharp | The image library Next.js uses for Image Optimization; it bundles libvips and librsvg | | glibc | The C library used by Debian, Ubuntu and node:*-slim images (Alpine uses musl instead) | | Active / Maintenance LTS | Next.js 16.4.x and 15.5.x, the two release lines that receive security fixes | TL;DR: how to patch Next.js now 1. Upgrade: npm install next@16.4.2 on 16.x (16.4.1 is the security release; 16.4.2 adds bug fixes) or npm install next@15.5.28 on 15.x. 2. Check sharp: run npm ls next sharp and confirm sharp@0.35.5 or later. On 15.x, run npm update sharp if it still shows 0.34.x. 3. Commit the lockfile, rebuild the image from scratch and redeploy. 4. Verify the running container, not just your laptop. !Decision chart: check the installed next version, then the three advisories with their affected versions, exposure conditions and workarounds, and the fix of next 16.4.1 or 15.5.28 with sharp 0.35.5 Is my Next.js app affected? You are affected if your installed next is 16.0.0 through 16.4.0, or 15.5.27 and earlier, or if the sharp copy Next.js loads is older than 0.35.5. Which bugs reach you depends on features: use cache needs Cache Components, the DoS needs at least one Server Action, and the SVG bug needs glibc Linux. Check the versions first: npm ls next sharp Then match your app against each advisory: - use cache RCE (GHSA-673q-mjm6-r5xr). Only Next.js 16 apps using the App Router with Cache Components can be exposed, and the trigger is a use cache function that returns multiple typed arrays. The advisory still says to upgrade immediately even if your application does not have similar functions. New apps from create-next-app 16.4 have Cache Components on by default, as I covered in Next.js use cache explained. - SVG Image Optimization RCE (CVE-2026-96889). A memory bug in librsvg, which sharp uses to decode SVG, can lead to RCE when Next.js runs on glibc-based Linux. That covers most Debian and Ubuntu based Docker images and VPS installs. By default Next.js does not optimize SVG images; apps that set images.dangerouslyAllowSVG: true are the clearest exposure, but the advisory is not limited to that setting, so patch either way. - Server Actions DoS (GHSA-p3m5-59mp-jv5r). Any App Router app with at least one Server Action, on Next.js 13 through 15.5.27 or 16.0.0 through 16.4.0. Pages Router apps and apps without Server Actions are not vulnerable. Why does npm audit say my app is safe? Because the advisory feed it reads may not list these issues yet. On October 17, 2026, I installed next@16.4.0, react and react-dom in an empty project and ran npm audit: $ npm audit --json | jq .metadata.vulnerabilities { "info": 0, "low": 0, "moderate": 0, "high": 0, "critical": 0, "total": 0 } Zero findings on a version with two open critical advisories. Treat npm audit, Dependabot and similar scanners as a backstop here and check the versions yourself, or use the script below. A script that checks your app against all three advisories I wrote this to check every app I run. It reads the installed next version, resolves sharp from Next.js's own folder (the copy the image optimizer actually loads), scans your source for use cache, use server and dangerouslyAllowSVG, and exits with code 1 if anything is unpatched, so you can drop it into CI. It has no dependencies. // check-next-security.mjs: checks a Next.js app against the October 14, 2026 advisories. // Usage: node check-next-security.mjs [projectDir] import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs' import { createRequire } from 'node:module' import path from 'node:path' const dir = path.resolve(process.argv[2] ?? '.') const req = createRequire(path.join(dir, 'package.json')) // Find node_modules/<pkg>/package.json the way Node would, without relying on "exports". const version = (pkg, from = req) => { for (const base of from.resolve.paths(pkg) ?? []) { const file = path.join(base, pkg, 'package.json') if (existsSync(file)) return JSON.parse(readFileSync(file, 'utf8')).version } return null } const cmp = (a, b) => { const [x, y] = [a, b].map((v) => v.split('-')[0].split('.').map(Number)) for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i] return 0 } const inRange = (v, lo, hi) => cmp(v, lo) >= 0 && cmp(v, hi) <= 0 // Scan source files for features the advisories depend on. const src = [] const walk = (d) => { for (const name of readdirSync(d)) { if (['node_modules', '.next', '.git', 'out', 'dist'].includes(name)) continue const p = path.join(d, name) if (statSync(p).isDirectory()) walk(p) else if (/\.(m?[jt]sx?)$/.test(name)) src.push(readFileSync(p, 'utf8')) } } walk(dir) const code = src.join('\n') const uses = { useCache: /['"]use cache(: \w+)?['"]/.test(code), serverActions: /['"]use server['"]/.test(code), svgOptimizer: /dangerouslyAllowSVG\s*:\s*true/.test(code), nextOg: /from ['"]next\/og['"]/.test(code), } const next = version('next') if (!next) { console.error('next is not installed in', dir); process.exit(2) } // Resolve sharp from next's own folder, since that is the copy the image optimizer loads. const nextPkg = (req.resolve.paths('next') ?? []).map((b) => path.join(b, 'next', 'package.json')).find(existsSync) const sharp = version('sharp', createRequire(nextPkg)) const major = Number(next.split('.')[0]) const checks = [ { id: 'GHSA-673q-mjm6-r5xr (critical): RCE in use cache returning typed arrays', vulnerable: inRange(next, '16.0.0', '16.4.0'), fix: 'upgrade next to 16.4.1+ (no workaround)', note: uses.useCache ? 'app uses "use cache"' : 'no "use cache" found, upgrade anyway', }, { id: 'CVE-2026-96889 / GHSA-2464-fhr4-w9xw (critical): RCE via SVG in image optimization', vulnerable: sharp ? cmp(sharp, '0.35.5') < 0 : false, fix: 'sharp must be 0.35.5+ (npm update sharp), next 16.4.1+ or 15.5.28+', note: `sharp ${sharp ?? 'not installed'}` + (uses.svgOptimizer ? ', dangerouslyAllowSVG on' : '') + (uses.nextOg ? ', uses next/og' : ''), }, { id: 'GHSA-p3m5-59mp-jv5r (high): DoS via Server Actions', vulnerable: inRange(next, '13.0.0', '15.5.27') || inRange(next, '16.0.0', '16.4.0'), fix: major >= 16 ? 'upgrade next to 16.4.1+' : 'upgrade next to 15.5.28+', note: uses.serverActions ? 'app defines Server Actions' : 'no "use server" found', }, ] console.log(`next ${next}, sharp ${sharp ?? 'not installed'} in ${dir}\n`) let bad = 0 for (const c of checks) { if (c.vulnerable) bad++ console.log(`${c.vulnerable ? 'VULNERABLE' : 'ok '} ${c.id}`) console.log(` ${c.note}${c.vulnerable ? `\n fix: ${c.fix}` : ''}`) } console.log(bad ? `\n${bad} issue(s) found. Patch, rebuild and redeploy.` : '\nAll three October 14, 2026 advisories are patched.') process.exit(bad ? 1 : 0) Run it from your project root with node check-next-security.mjs. This is the real output on a Next.js 16.4.0 app with one use cache function and one Server Action, before and after npm install next@16.4.2: $ node check-next-security.mjs next 16.4.0, sharp 0.35.4 in /app VULNERABLE GHSA-673q-mjm6-r5xr (critical): RCE in use cache returning typed arrays app uses "use cache" fix: upgrade next to 16.4.1+ (no workaround) VULNERABLE CVE-2026-96889 / GHSA-2464-fhr4-w9xw (critical): RCE via SVG in image optimization sharp 0.35.4 fix: sharp must be 0.35.5+ (npm update sharp), next 16.4.1+ or 15.5.28+ VULNERABLE GHSA-p3m5-59mp-jv5r (high): DoS via Server Actions app defines Server Actions fix: upgrade next to 16.4.1+ 3 issue(s) found. Patch, rebuild and redeploy. $ npm install next@16.4.2 && node check-next-security.mjs next 16.4.2, sharp 0.35.5 in /app ok GHSA-673q-mjm6-r5xr (critical): RCE in use cache returning typed arrays app uses "use cache" ok CVE-2026-96889 / GHSA-2464-fhr4-w9xw (critical): RCE via SVG in image optimization sharp 0.35.5 ok GHSA-p3m5-59mp-jv5r (high): DoS via Server Actions app defines Server Actions All three October 14, 2026 advisories are patched. The script checks versions and source patterns; it is not an exploit test. A clean result means you are on patched versions, which is what the advisories ask for. How do you upgrade Next.js 16 to the patched version? Install the latest 16.4 patch, then confirm sharp moved with it. Next.js 16.4.1 raised its sharp requirement to ^0.35.5 (16.4.0 accepted ^0.35.4; I read both from the npm registry), so npm has to upgrade sharp when you upgrade next. npm install next@16.4.2 npm ls next sharp In my test, a project whose lockfile held next@16.4.0 and sharp@0.35.4 came out of npm install next@16.4.1 with sharp@0.35.5, as expected. Run the same check with pnpm why sharp or yarn why sharp if you use those package managers. eslint-config-next should match your next version, so bump it in the same commit. If you have not moved to 16 yet, the version 16 upgrade guide covers the breaking changes; that is a bigger job than this patch, so patch 15.x first. How do you patch Next.js 15 safely? Upgrade to 15.5.28, then check sharp separately, because upgrading next alone may not fix the SVG bug. Next.js 15.5.28 declares sharp as ^0.34.3 || ^0.35.5, so an existing lockfile that already resolved sharp 0.34.x still satisfies it, and npm keeps it. This is what happened on a real project with next@15.5.27 and sharp@0.34.5 in the lockfile: $ npm install next@15.5.28 $ npm ls sharp └─┬ next@15.5.28 └── sharp@0.34.5 # still vulnerable $ npm update sharp $ npm ls sharp └─┬ next@15.5.28 └── sharp@0.35.5 # fixed If sharp is a direct dependency in your package.json, run npm install sharp@0.35.5 instead. To force one version across a monorepo, add an override: { "overrides": { "sharp": "0.35.5" } } pnpm uses pnpm.overrides and Yarn uses resolutions for the same job. How do you patch a self-hosted Next.js app in Docker or Coolify? Update the lockfile, rebuild the image without reusing old layers, redeploy, then check the versions inside the running container. A dependency fix only protects you once the container that serves traffic contains it, and a Docker build that copies package-lock.json and runs npm ci installs exactly what the lockfile says. !Six-step patch flow for self-hosted Next.js: update deps, check the lockfile, commit it, rebuild the image, redeploy with Coolify or docker compose, and verify the versions inside the container 1. Update and verify locally. npm install next@16.4.2, npm ls next sharp, run the checker. 2. Commit package-lock.json. Without it, your build server resolves whatever it resolved last time. 3. Rebuild the image. With the standalone output used in my production Next.js Dockerfile, next build copies sharp and its native libvips package into .next/standalone/node_modules. In my build that was @img/sharp-libvips-linux-x64, the glibc variant. An image built before the patch keeps the old sharp until you rebuild it. 4. Redeploy. In Coolify, push the commit or click Redeploy. To skip cached layers for this one build, trigger a forced deploy, for example through the Coolify API with GET /api/v1/deploy?uuid=<app-uuid>&force=true. My self-host Next.js with Coolify guide covers the deploy setup. With plain Docker, run docker compose build --no-cache && docker compose up -d. 5. Verify the running container. In a standalone image the app lives in /app, so: docker exec <container> node -e "console.log(require('/app/node_modules/next/package.json').version, require('/app/node_modules/sharp/package.json').version)" Requiring the package.json files by absolute path works even though sharp 0.35.5 does not export ./package.json. I checked the same command against a standalone build of Next.js 16.4.2; it printed 0.35.5 16.4.2 for sharp and next. Adjust the path if your Dockerfile copies the app somewhere other than /app. What if you cannot upgrade Next.js today? Only the SVG bug has an interim mitigation; the use cache RCE and the Server Actions DoS have no workaround, so plan the upgrade within hours, not weeks. In the meantime: | Bug | Interim step | Source | | --- | --- | --- | | SVG / sharp RCE | Make sure sharp is 0.35.5 or later, even without upgrading next | Next.js advisory | | SVG / sharp RCE | In your own code that calls sharp, block SVG decoding with sharp.block({ operation: ['VipsForeignLoadSvg'] }) | sharp advisory | | SVG / sharp RCE | Turn off images.dangerouslyAllowSVG if you do not need optimized SVGs | Image docs | | use cache RCE | None. Upgrade. | Next.js advisory | | Server Actions DoS | None. Upgrade. Rate limits only reduce the blast radius. | Next.js advisory | I tested the sharp.block call with sharp 0.35.5: before it, a small SVG converted to a 100-byte PNG; after it, the same call failed with Input buffer contains unsupported image format. import sharp from 'sharp' // Refuse to decode SVG input anywhere this sharp instance is used. sharp.block({ operation: ['VipsForeignLoadSvg'] }) The sharp advisory adds one hardening note: whether the librsvg bug can reach code execution depends partly on the Node.js binary. It recommends a node built as a Position Independent Executable (PIE), notes that most Linux distribution packages already are, and warns that the official Node.js binaries are not. How do you know whether you were attacked? The advisories publish no indicators of compromise and do not mention exploitation in the wild, so look for the side effects each bug would leave, and re-check the advisories for updates. - DoS: sustained 100% CPU on a Next.js process together with slow or failed requests, especially POST requests carrying a Next-Action header. - SVG RCE: unexpected requests to /_next/image with .svg sources, crashes of the Node.js process during image optimization, and new child processes or outbound connections from the container. - Any RCE: unknown files in the container filesystem, changed environment variables or secrets used from new IPs. If you find any of these, rotate secrets (database URLs, API keys, NEXT_SERVER_ACTIONS_ENCRYPTION_KEY) after patching. Security habits that make the next patch faster This was an out-of-band release that landed eight days after 16.4.0 shipped on October 6. A few habits keep the next one to a ten-minute job: - Stay on an LTS line. Security fixes land on 16.4.x (Active LTS) and 15.5.x (Maintenance LTS). - Turn on upgrade nudges. Next.js 16.4 added experimental.agentUpgrade, whose default 'security' policy reminds you during next dev and next build when an upgrade fixes a known vulnerability in your installed version (Next.js 16.4 notes). - Run the checker in CI. Exit code 1 fails the build when a vulnerable version is installed. - Pin and commit lockfiles, then rebuild images from them so the running version is the one you reviewed. - Keep Server Actions small and authenticated. The DoS needs only one action to exist, but auth and input validation limit what any future bug can reach; see Server Actions vs Route Handlers. Common mistakes when patching | Mistake | What happens | Fix | | --- | --- | --- | | Upgrading react instead of next | Next.js keeps its bundled RSC runtime | Upgrade next | | Upgrading 15.x to 15.5.28 and stopping | sharp 0.34.x can stay in the lockfile | npm update sharp, confirm 0.35.5+ | | Trusting npm audit | It reported 0 issues on unpatched 16.4.0 | Check versions or run the script | | Not committing the lockfile | CI or Coolify rebuilds the old versions | Commit package-lock.json | | Rebuilding from a cached layer | Old node_modules layer is reused | Build once with no cache | | Checking only the laptop | Production still runs the old image | docker exec into the running container | Related: I packaged the checker script above as a reusable agent skill, so Claude Code, Cursor, Codex or Copilot can run the check and patch for you. See Agent Skills: how to write a SKILL.md. Related: running an MCP server next to your Next.js app? MCP security in 2026 covers this month's MCP SDK OAuth advisories and how to fix them. Key takeaways - The October 14, 2026 update fixes two critical RCEs (use cache typed arrays and SVG via sharp) and a high-severity Server Actions DoS. - Patched versions are Next.js 16.4.1+ and 15.5.28+, with sharp 0.35.5 or later. - On 15.x, upgrading next can leave sharp 0.34.x installed; run npm update sharp and verify. - npm audit reported nothing for unpatched next@16.4.0 on October 17, 2026, so check versions directly. - A self-hosted fix only counts after you rebuild the image, redeploy and verify the versions inside the running container. --- Originally published on fabwaseem.com.

  4. lovell/sharp 面向 Node.js 的开源图片处理库。 批量裁图、改尺寸,一次生成多种格式。 lovell/sharp

    5 views
  5. 把大佬的宝藏做成了一个开箱即用的skill,大家可以来玩呀 GitHub:JuniverseLi/motion-studio-skill

    2 likes · 1 repost · 8 views
Trending awards, every completed day and week
Gold
01st place finishes
Silver
02nd place finishes
Bronze
13rd place finishes
Awarded repositories
1Owned by lovell now

Repositories

Ordered by gold, then silver, then bronze.

  1. sharpBest: 3rd by stars, 256 stars001

Every award

Top-three finishes of these repositories in completed trending days and weeks, newest first.

  1. #3 by starsDaily trending
    256 stars
    sharp

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.