fix: dialogs rendered EMPTY - relative project path caused a silent 403
THE ACTUAL CAUSE of "codegit is just a black box". Sections 4 and 5 were real bugs but
neither was it; this is.
CHAIN
1. install/process.php stores a RELATIVE folder name -> projects.db {"Blog":"blog"}
2. common.php::loadState() does $projectPath = reset($projectList) -> "blog", and
SESSION("projectPath","blog") PERSISTS it, so the bad value sticks across logins
3. filetree puts it straight into the DOM: <a id="project-root" data-path="blog">
4. codegit sends repo="blog"; getWorkspacePath("blog") fails checkPath() ->
traits/path.php:67 Common::send(403, "Client does not have access.")
5. modal.js SILENTLY discards it:
settled: function(reply,status){ if (status !== 200) return; ... }
No toast, no console error, #content left empty.
That last step is why it presents as an empty black dialog rather than an error - the request
fails and nothing reports it. Which is exactly why it survived the CSS fixes.
PINNED DOWN BY CAPTURING THE REAL REQUEST, not by reading CSS:
POST /dialog.php {"target":"codegit","action":"codegit","repo":"blog"} -> 403
#content innerHTML length: 0
getWorkspacePath("blog") -> 403
projects store -> {"Blog":"blog"}
FIX (scripts/fix-project-path.sh) - both parts required:
A. resolve the path in loadState and re-store it, which also HEALS sessions already
holding the relative value (and re-persists so it stays healed)
B. make the projects store absolute, so array_search() can still map path -> name for
the tree label. Fixing only (A) would silently break the label: the path would resolve
but array_search would return false and the tree would show an empty project name.
VERIFIED from a deliberately poisoned session ("blog"): it self-heals to the absolute path,
dialog returns 200, #content fills with 1758 chars. An EXISTING browser session heals on its
next page load - no hard refresh or re-login needed.
Tests: 47 passed, 0 failed, 1 accepted-risk
+ projects store must hold an absolute path
+ loadState must resolve a relative projectPath
Also marks section 4 as superseded so a future reader doesn't stop at the CSS explanation.
This commit is contained in:
+115
-10
@@ -229,7 +229,11 @@ Worth doing if the dialog starts 500-ing again after editing a macro in the UI.
|
||||
**Symptom:** the CodeGit dialog appeared as a mostly-empty black rectangle. Nothing was *broken* —
|
||||
it was unreadable, which reads as "broken".
|
||||
|
||||
**This was NOT a codegit bug.** Verified exhaustively first:
|
||||
> **⚠️ Superseded by §6.** This section fixed a real layout bug, but it was NOT the cause of the
|
||||
> empty CodeGit dialog. The actual cause was a relative project path making the request 403, which
|
||||
> the modal silently swallowed. Both are fixed; if a dialog is ever blank again, read §6 first.
|
||||
|
||||
**This was NOT a codegit bug in the JS sense.** Verified exhaustively first:
|
||||
|
||||
```
|
||||
all four panels return real content through the authenticated API:
|
||||
@@ -328,7 +332,108 @@ change the file's mtime (`touch`) and the URL changes by itself.
|
||||
|
||||
---
|
||||
|
||||
## 6. Checking for plugin problems in future
|
||||
## 6. 🔴 Every dialog rendered EMPTY (CodeGit "just a black box")
|
||||
|
||||
**Symptom:** CodeGit — and, it turns out, the Macro / Market / Settings dialogs — opened as an empty
|
||||
black area. No error, no text, nothing. Reported as *"codegit still no go just a black box"*, twice,
|
||||
and it survived the CSS fix in §4, because it was never a CSS problem.
|
||||
|
||||
**This is the bug that actually mattered.** §4 and §5 were real defects, but neither was the cause.
|
||||
|
||||
### The chain
|
||||
|
||||
1. **The installer stores a RELATIVE path.** `components/install/process.php` keeps the folder name
|
||||
as given, so the project store held `{"Blog": "blog"}` — not an absolute path.
|
||||
2. **`loadState()` hands it to the UI unmodified and persists it** (`common.php`):
|
||||
|
||||
```php
|
||||
$projectPath = reset($projectList); // "blog"
|
||||
...
|
||||
SESSION("projectPath", $projectPath); // stored -> the bad value STICKS across logins
|
||||
```
|
||||
|
||||
3. **The file tree puts it straight into the DOM** (`components/filetree/init.js`):
|
||||
|
||||
```js
|
||||
let path = atheos.current.projectPath;
|
||||
`<a id="project-root" data-type="root" data-path="${path}">` // data-path="blog"
|
||||
```
|
||||
|
||||
4. **CodeGit then sends `repo="blog"`**, and `getWorkspacePath("blog")` fails its access check:
|
||||
|
||||
```
|
||||
traits/path.php:67 Common::send(403, "Client does not have access.");
|
||||
```
|
||||
|
||||
5. **`modal.js` silently discards the failure** — this is what makes it look like a "black box"
|
||||
rather than an error:
|
||||
|
||||
```js
|
||||
settled: function(reply, status) {
|
||||
if (status !== 200) return; // no message, no toast, no console error
|
||||
content.html(reply);
|
||||
```
|
||||
|
||||
So the request 403s, nothing is rendered, and nothing is reported. The dialog is left with an empty
|
||||
`<section id="content">`.
|
||||
|
||||
### How it was pinned down
|
||||
|
||||
Not by reading CSS. By capturing the real request/response from a scripted browser:
|
||||
|
||||
```
|
||||
POST /dialog.php {"target":"codegit","action":"codegit","repo":"blog"}
|
||||
-> 403 (and #content innerHTML length: 0)
|
||||
```
|
||||
|
||||
A relative path was reproduced directly:
|
||||
|
||||
```
|
||||
is_dir("blog") from the app root -> NO
|
||||
getWorkspacePath("blog") -> 403 Client does not have access.
|
||||
projects store -> {"Blog": "blog"}
|
||||
```
|
||||
|
||||
### Fix (`scripts/fix-project-path.sh`)
|
||||
|
||||
A relative value in the store is **legitimate** — Atheos's own project-creation code resolves
|
||||
relative → `WORKSPACE/<name>`. `loadState()` simply never does. Two changes, both required:
|
||||
|
||||
**A. Resolve at load time, and re-store it** — this heals sessions already holding the bad value:
|
||||
|
||||
```php
|
||||
if (!Common::isAbsPath($projectPath)) {
|
||||
$projectPath = Common::getWorkspacePath($projectPath, true);
|
||||
SESSION("projectPath", $projectPath);
|
||||
}
|
||||
```
|
||||
|
||||
**B. Make the store absolute** (`{"Blog": "/var/www/atheos/workspace/blog"}`) so that
|
||||
`array_search($projectPath, $projectList)`, which `loadState()` uses to derive the project *name*
|
||||
for the tree label, keeps working. Fixing only (A) would silently break the label — the path would
|
||||
resolve but `array_search` would return `false`, so the tree would show an empty name.
|
||||
|
||||
**Verified** by starting from a deliberately poisoned session (`projectPath="blog"`) and confirming
|
||||
it self-heals:
|
||||
|
||||
```
|
||||
projectPath : /var/www/atheos/workspace/blog <- healed from "blog"
|
||||
data-path : /var/www/atheos/workspace/blog
|
||||
dialog.php : 200 with content
|
||||
#content : 1758 chars
|
||||
visible : CodeGit Overview Log Transfer Configure Commit Amend ...
|
||||
```
|
||||
|
||||
**An existing browser session heals on its next page load** — no hard refresh or re-login needed.
|
||||
|
||||
### Worth remembering
|
||||
|
||||
A 403 from `getWorkspacePath()` presents as *"the dialog is empty"*, with no error anywhere. If any
|
||||
Atheos dialog is ever blank again, check the **project path** first, then the response code — not the CSS.
|
||||
|
||||
---
|
||||
|
||||
## 7. Checking for plugin problems in future
|
||||
|
||||
The plugin's JS is bundled into `public/plugins.min.js` (built by `classes/sourcemanager.php`), and
|
||||
its rightbar button is rendered by `components/sidebars/sb-right.php` from `plugin.json`. Both were
|
||||
@@ -362,12 +467,12 @@ verify them before trusting it.
|
||||
|
||||
---
|
||||
|
||||
## 7. Follow-ups not done (recorded so they are not forgotten)
|
||||
## 8. Follow-ups not done (recorded so they are not forgotten)
|
||||
|
||||
These came out of review. They are **operator decisions**, not agent ones — several change what the
|
||||
editor can do.
|
||||
|
||||
### 7.0 Branch protection on `master` — ACCEPTED RISK
|
||||
### 8.0 Branch protection on `master` — ACCEPTED RISK
|
||||
|
||||
Deliberately **not** enabled while the editor is LAN-only (operator decision). Full reasoning and the
|
||||
"enable it before you expose this host" trigger are in
|
||||
@@ -376,12 +481,12 @@ Deliberately **not** enabled while the editor is LAN-only (operator decision). F
|
||||
The suite still probes for it on every run and reports `ACCEPTED-RISK` (not a failure) while it is
|
||||
off, so the recommendation self-cancels the moment it is enabled.
|
||||
|
||||
### 7.1 Remove the Terminal plugin — ✅ DONE
|
||||
### 8.1 Remove the Terminal plugin — ✅ DONE
|
||||
|
||||
Removed through the market's own code path once §1 was fixed (see §2). A browser shell was the largest
|
||||
attack surface in the container and macros already cover command execution.
|
||||
|
||||
### 7.2 ❌ `disable_functions` cannot be "on by default" here — measured
|
||||
### 8.2 ❌ `disable_functions` cannot be "on by default" here — measured
|
||||
|
||||
Review asked for `php.ini` to disable `exec`, `shell_exec`, `passthru`, `system` by default.
|
||||
**This would break the editor's core function.** Evidence:
|
||||
@@ -398,18 +503,18 @@ turns the editor into a text editor that cannot commit. So it stays **a delibera
|
||||
you would also lose macros and git integration, which is why S-7 already says *"test, do not
|
||||
blind-apply"*.
|
||||
|
||||
### 7.3 Rotate the terminal password automatically
|
||||
### 8.3 Rotate the terminal password automatically
|
||||
|
||||
If the plugin is kept, `provision.sh` should generate a random password rather than leaving the
|
||||
upstream default. Not implemented: it would hardcode a secret into a repo file, which is the same
|
||||
class of problem this file already documents.
|
||||
|
||||
### 7.4 Deploy key could be read-only
|
||||
### 8.4 Deploy key could be read-only
|
||||
|
||||
Review suggested making the deploy key read-only if writes are unnecessary. **They are necessary** —
|
||||
that is the whole point of the writer-branch workflow (macro 2 pushes). Not applicable.
|
||||
|
||||
### 7.5 The real upstream fix for §3
|
||||
### 8.5 The real upstream fix for §3
|
||||
|
||||
`class.macro.php::save()` should write `[]` instead of `"N/A"` for folder macros. See §3 — the fix is
|
||||
`class.macro.php::save()` should write `[]` instead of `"N/A"` for folder macros. See §4 — the fix is
|
||||
currently applied to the data, so it survives updates, at the cost of being reversible from the UI.
|
||||
|
||||
Reference in New Issue
Block a user