Commit 94ddb09
harden(clients): preserve file mode (0600) + flush on atomic config writes (#545)
* harden(clients): preserve file mode on atomic config writes, default new configs to 0600
McpAtomicWrite created the tmp file with the umask default (0644) and used
DirAccess.copy_absolute for the backup, neither of which preserves the prior
file's permissions. The Claude Code strategy writes directly into ~/.claude.json,
which the Claude CLI creates 0600 (it holds OAuth creds + history); every
"Configure" click silently relaxed it to 0644 and left a 0644 .backup, leaking
it on shared machines.
Capture the prior file's POSIX mode before replacing it and re-apply it to the
swapped-in file and the backup, so a 0600 config stays 0600 (and a 0644 config
stays 0644 — we preserve, we don't clamp). Brand-new configs default to
owner-only 0600 since these files routinely carry tokens. The mode is set on
the tmp inode before the rename so the target is never briefly world-readable.
On platforms without POSIX permissions (Windows) get/set_unix_permissions
no-op and the logic is inert.
GDScript tests (run against live Godot 4.6.3, real on-disk chmod): preserve
0600 on rewrite, backup inherits 0600, new file defaults to 0600, and a 0644
file stays 0644. Skipped on Windows. Full suite: 1365/1382 passed, 0 failed.
Addresses advisory GHSA-gw6h-44vw-g5rc.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* harden(clients): flush the temp file before the atomic rename
store_string buffers in Godot's FileAccess; close() flushes, but doing the
flush() explicitly before the rename makes the ordering intent clear and
ensures the bytes are handed to the OS before the swap. Godot exposes no
fsync, so durability against power loss still depends on the OS cache — the
comment documents that residual. Pairs with the permission-preservation fix
on this branch (atomic config writes).
Addresses review finding TC-7 (#531).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* harden(clients): address Copilot review on atomic-write permission mode
- Clarify why get_unix_permissions == 0 maps to the 0600 floor rather than
"preserving" 0000: these are config files the plugin must read/write, 0000 is
unusable, re-applying it would lock the owner out, and 0600 never widens
access. (A genuinely-0000 file can't reach a rewrite anyway — the config
strategies' read-first guard refuses it first.) Copilot's literal "preserve
0000" suggestion would regress: _apply_mode skips mode 0, leaving the file at
copy_absolute's umask default (potentially 0644).
- _apply_mode now captures set_unix_permissions' return and push_warning()s on a
real failure (not the Windows ERR_UNAVAILABLE no-op), so permission hardening
on a sensitive config no longer fails completely silently.
Live Godot 4.6.3: clients suite 97/97.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* harden(clients): close the temp-file world-readable window before writing secrets
Copilot re-review: the temp file was created at the umask default, the config
contents written, and only then chmod'd — leaving a brief window where secrets
sat on disk world-readable under path+".tmp". Now chmod the still-empty temp
inode to the target mode BEFORE store_string, so the contents never land under
a permissive mode. A chmod issued while the FileAccess handle is open does not
reliably stick inside the editor, so the post-close apply is kept as the
authoritative one (the rename then preserves the mode). Documented the small
residual window on the .backup copy (copy_absolute can't be created pre-chmod'd;
it duplicates already-0600 bytes in the user's own dir, chmod'd immediately).
Live Godot 4.6.3: clients suite 97/97.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent cff4c3f commit 94ddb09
2 files changed
Lines changed: 183 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
23 | 35 | | |
24 | 36 | | |
25 | 37 | | |
26 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
27 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
28 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
29 | 59 | | |
30 | 60 | | |
31 | 61 | | |
32 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
33 | 71 | | |
34 | | - | |
35 | 72 | | |
36 | 73 | | |
37 | 74 | | |
38 | 75 | | |
39 | 76 | | |
| 77 | + | |
40 | 78 | | |
41 | 79 | | |
42 | 80 | | |
| |||
46 | 84 | | |
47 | 85 | | |
48 | 86 | | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
49 | 90 | | |
50 | 91 | | |
51 | 92 | | |
| |||
59 | 100 | | |
60 | 101 | | |
61 | 102 | | |
| 103 | + | |
62 | 104 | | |
63 | 105 | | |
64 | 106 | | |
| |||
76 | 118 | | |
77 | 119 | | |
78 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
79 | 153 | | |
80 | 154 | | |
81 | 155 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1401 | 1401 | | |
1402 | 1402 | | |
1403 | 1403 | | |
| 1404 | + | |
| 1405 | + | |
| 1406 | + | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + | |
| 1410 | + | |
| 1411 | + | |
| 1412 | + | |
| 1413 | + | |
| 1414 | + | |
| 1415 | + | |
| 1416 | + | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
| 1508 | + | |
| 1509 | + | |
| 1510 | + | |
1404 | 1511 | | |
1405 | 1512 | | |
1406 | 1513 | | |
| |||
0 commit comments