Commit Graph

126 Commits

Author SHA1 Message Date
dillard c1fd53845f Build the console application from the container
The console application was created with a hand-written list of eight
constructor arguments, which broke whenever Nextcloud changed its
constructor. Resolve the arguments by type from the container instead,
passing our own request so the console still gets an argv for its
ConsoleEvent.
2026-09-30 16:11:53 +02:00
dillard 2b57349519 Refresh the screenshots
Replace the old screenshot with a current one of the terminal running
occ, and add the start screen as a second screenshot for the app store.
2026-09-30 16:08:22 +02:00
dillard 45c97653ca Trim the compatibility matrix and add 0.4.5
Keep the last release under the old id and the latest releases; the
older entries stay in the git history of the README.
2026-09-30 16:03:00 +02:00
dillard 363eb0de11 Document the security model
Only admins can use the app, so who gets admin rights decides who can use
it. The database should run as a scoped user without superuser or
server file/program rights, since the SQL checks are an extra layer.
Explain why sensitive occ commands are not blocked: an admin can reach
the same information in other ways, so a blocklist would only give a
false sense of security and make the app less useful.
v0.4.5
2026-09-30 11:39:30 +02:00
dillard bea30b94c9 Confirm MySQL SET forms that change server state
SET PASSWORD, SET GLOBAL and SET PERSIST (also as @@GLOBAL.x) change
the server or the database account rather than the session, so they
need confirmation like other writes. Session variables stay a read.
2026-09-30 10:44:24 +02:00
dillard 9861313961 Treat DESCRIBE as a read
DESCRIBE and DESC only show a table on MySQL/MariaDB. SQLite's PRAGMA
stays out of the read list because it can also change settings.
2026-09-30 04:00:33 +02:00
dillard f89ced651b Show rows for all reads and confirm nextval()
SHOW, EXPLAIN, VALUES and TABLE count as reads now but their rows were
dropped and reported as a write; every read except SET now returns its
rows. nextval() advances a sequence, so it needs confirmation like
setval().
2026-09-30 03:59:11 +02:00
dillard 10055c9358 Only treat comment markers outside literals as comments
removeComments() used regexes that also matched "--" and "/*" inside
string literals. A literal like '--' or '/*' then removed the code after
it from the checks while PostgreSQL still ran it, e.g.
COPY (SELECT '--') TO PROGRAM 'sh' passed the forbidden-construct check,
and a DELETE in a CTE between '/*' and '*/' literals skipped the
confirmation.

Comments and literals are now found by one scanner that walks the SQL
the way the database does ('...', E'...', "...", $tag$...$tag$, -- and
block comments), shared by removeComments(), removeLiterals() and
splitStatements().
2026-09-30 03:46:34 +02:00
dillard f4b7484e2f Split SQL batches correctly around dollar quotes and comments
splitStatements() only knew 'strings' and "identifiers", so a DO block
or function body ($$ ... ; ... $$) was cut at its inner semicolons and
failed, and a quote inside a comment (-- don't) opened a string that
never closed. It now also skips dollar-quoted bodies, E'...' strings
with backslash escapes, and comments. It moves to SqlGuard so it is
tested with the other checks.

OccController::run() now catches Throwable, so an Error thrown by a
command is logged and returned like an Exception instead of ending in
an HTTP 500.

Not changed on purpose: showing rows for statements with RETURNING or
data-modifying CTEs, and failure handling in the Makefile release
targets. Both came up in the same review but are unrelated to the
security fixes in this release.
2026-09-30 03:32:58 +02:00
dillard f68b1658be Harden SQL mode checks and sanitize occ output
A security review found three issues:

- Comments were removed without a space before matching the forbidden
  patterns, so "TO/**/PROGRAM" became "TOPROGRAM" and passed the
  COPY ... PROGRAM check while PostgreSQL still ran it. Comments are
  now replaced by a space, and the COPY file check also covers E'...',
  U&'...' and dollar-quoted file names.
- Only statements starting with DELETE or UPDATE asked for
  confirmation, so a leading block comment, a data-modifying CTE
  (WITH x AS (DELETE ...)) or DROP/TRUNCATE/INSERT ran without it.
  Every statement that is not a plain read now needs confirmation,
  decided on the server.
- occ output could contain escape sequences, backspaces and URLs from
  user-controlled text (display names, file names) that rewrite or hide
  text or show a clickable link. Only color codes are kept now, and
  URLs are no longer turned into links.

The checks move to SqlGuard so they can be tested on their own. They
remain defense in depth: dynamic SQL can always get around a text
check, so the database role should not have superuser rights,
pg_execute_server_program or pg_read/write_server_files. A read that
starts with WITH now shows its rows like a SELECT.
2026-09-30 03:15:35 +02:00
dillard 3fc8601344 Keep occ output brackets escaped through the formatters
The overtyping and from_ansi formatters unescape brackets before they
parse the text, so escaping them beforehand was not enough: literal
brackets were still read as formatting. Enable
unixFormattingEscapeBrackets so both formatters escape the text again
after parsing. Verified in the browser: Symfony's "[alias]" in
`occ list` stays in place, ANSI colors still work, and formatting in
the output text (colors, links) is no longer applied.

Bump the version to 0.4.4 so browsers load the new script.
2026-09-30 02:25:13 +02:00
dillard 635cdd4b24 Escape brackets in occ output before rendering
jQuery Terminal reads "[" and "]" as its own formatting syntax. occ
output was echoed as is, so literal brackets, such as Symfony's
"[alias]" in `occ list`, closed the color formatting of the next line
early and moved text to the wrong place. Text from the output, like a
display name, could also inject terminal formatting.

Escape the brackets in the text and keep the ANSI sequences, so the
colors still work.
2026-09-30 02:07:46 +02:00
dillard 76368f154e Add check-on-branch guard: HEAD's branch, not just its cleanliness
git archive HEAD packages whatever commit the checkout currently points to,
regardless of branch - check-clean-tree only guards uncommitted changes, not
HEAD being a feature branch, old tag, PR checkout, or detached HEAD entirely.
Wired into source/appstore/version alongside the existing guards.
Override via RELEASE_BRANCH=<branch> for an intentional non-main release.
2026-09-27 00:45:18 +02:00
dillard a607426a9c Only load vendored jQuery on NC34+, verified against NC32/33/34 in testbak/
Core still provides a working jQuery on NC32/33, but via a read-only
window.$ getter - loading our own vendored copy there too made its UMD
wrapper throw trying to reassign window.$ (confirmed on real containers,
harmless in practice since this app attaches via window.jQuery, but a real
avoidable console exception). Gate the extra script() call on
ServerVersion::getMajorVersion() >= 34 instead.

Re-tested both sides of the boundary: NC32 no longer requests jquery.js and
has zero console errors; NC34 still requests it and still works.
v0.4.3
2026-09-27 00:24:47 +02:00
dillard 35e1f5df32 Fix NC35 fatal in OccOutput, verify+narrow supported version range to 32-35
testbak/ (local, non-AIO docker compat rigs for NC32/33/34/35) found a real
fatal on NC35: OccOutput::setErrorOutput() didn't declare the ": void"
return type the Symfony Console version NC35 bundles now requires on
ConsoleOutputInterface. Fixed by declaring it - compatible with older
Symfony/NC versions too since it only narrows an untyped parent signature.

32/33 were also verified functional (the previously-suspected jQuery
getter-conflict console exception on those versions is real but harmless -
the app attaches via window.jQuery, not window.$).

min-version/max-version narrowed from 30/34 to 32/35: 30/31 were never
actually tested even before this session, so keeping them was an unverified
claim; 35 is now genuinely verified rather than guessed.
2026-09-27 00:17:28 +02:00
dillard 4b84be3cb7 Fix sixth Codex finding: check-clean-tree only guarded version, not source/appstore
Running make appstore/source/dist directly (bypassing make version)
still had the stale-archive risk check-clean-tree was added for:
app_name/paths read from a dirty working tree while git archive HEAD
packages the last commit. Added check-clean-tree as a prerequisite to
source and appstore directly, not just transitively through version.
2026-09-26 17:18:26 +02:00
dillard 93244a0d4d Fix fifth Codex finding: app_name silently empty without xpath installed
app_name now comes from xpath against appinfo/info.xml (needed so
packaging works regardless of what this checkout is named - see the
git-archive fix). If xpath isn't installed, $(shell ...) just returns
an empty string rather than failing the build: --prefix=$(app_name)/
would silently become --prefix=/, producing a broken, wrongly-rooted
archive with no indication anything went wrong. Previously app_name
came from the checkout directory name, so this failure mode didn't
exist. Added a check-app-name prerequisite to source/appstore/sign
that fails loudly if app_name is empty.
2026-09-26 17:14:19 +02:00
dillard 8fc4942517 Fix fourth Codex finding: reject a dirty tree before releasing
VERSION/app_name are read live from the working tree's info.xml, but
the packaged/signed content comes from git archive HEAD (the last
commit). Bumping <version> without committing first would tag and
sign an artifact named/keyed after the new version while its actual
contents are still the previous commit's. make version now refuses to
proceed if there are any uncommitted changes to tracked files.
2026-09-26 17:08:22 +02:00
dillard 95cdd06426 Fix third Codex finding: make version hardcoded a non-portable remote name
git push fork v\$(VERSION) only works on this maintainer's own machine,
where "fork" happens to be configured (and where "origin" still means
the stale upstream, from how this repo was originally forked). A plain
clone via the README's own instructions only ever creates "origin",
pointing at dillardblom/occweb - so make version would tag locally,
then fail at the push step before packaging/signing ever ran.

Made the target remote configurable via RELEASE_REMOTE, defaulting to
"origin" (correct for a normal clone), overridable for this specific
checkout's non-standard remote layout.
2026-09-26 17:01:19 +02:00
dillard 6016d0916c Fix second Codex finding: packaging symlink produced a dangling-symlink-only archive
The ensure-app-name-symlink fix from the previous commit created
../extended_occweb as a symlink so the inherited "../$(app_name)" tar
references would resolve regardless of the checkout's real directory
name - but the tar invocations ran without --dereference, so GNU tar
stored the symlink itself rather than following it into the real
files. make source/appstore therefore produced a 1-entry archive
containing nothing but a dangling absolute symlink, unusable on any
other machine. Caught by a second independent Codex pass; my own
"make appstore" test after the first fix only checked the archive's
top-level entry name, not its actual file count, so this went
unnoticed.

Rather than patch this with --dereference, replaced the whole
symlink+raw-tar approach with `git archive --prefix=$(app_name)/
HEAD`: it sets the correct top-level folder name directly (no symlink
needed at all), and - caught during this fix's own verification,
not by either Codex pass - it also closes a separate real leak the
symlink approach had: tarring the checkout directory directly
included every untracked/gitignored file too (e.g.
notes/deploy-nc01.md, which exists specifically because it must
never leave this machine). `git archive` only ever includes tracked
files. Dev/CI-only files (tests/, Makefile, composer.*, phpunit*.xml,
.travis.yml) are now stripped via `export-ignore` entries in a new
.gitattributes instead of a long list of tar --exclude flags repeated
per target - also means source and appstore now produce identical
output, which is fine given neither is part of our actual release
flow yet.

Verified end to end this time: extracted the resulting archive and
confirmed file count, absence of symlinks, absence of tests/notes/
dev files, and correct info.xml content - not just the archive's
top-level listing.
2026-09-26 16:55:55 +02:00
dillard 422748cace Fix Codex-review findings: Makefile packaging, git archive main hardcode
Independent Codex review of the app-id rename found real gaps our own
review missed:

- Makefile: app_name was derived from the checkout directory's own
  basename, not the app id in appinfo/info.xml. Our actual working
  checkout is still named "occweb" (matches the repo name), so
  building from it would have produced an "occweb"-named/rooted
  archive under the new "extended_occweb" id. Fixed by deriving
  app_name from info.xml via xpath (same technique already used for
  VERSION), and added an ensure-app-name-symlink step so the
  Makefile's inherited "../$(app_name)" references (used throughout
  the source/appstore tar exclude patterns) keep resolving correctly
  regardless of what the checkout directory is actually named.
  Verified end to end: `make appstore` now produces
  extended_occweb.tar.gz with extended_occweb/ as its single top-level
  folder, matching the App Store's stated requirement.
- Makefile: sign target had "occweb" hardcoded into both the
  certificate path and the archive filename - broken independent of
  the app_name fix above. Now derived from the same variables.
- Makefile: version target pushed the release tag to the "origin"
  remote, which in this repo is the stale upstream
  (fanategorius/occweb), not somewhere we want to push. Changed to
  "fork" (dillardblom/occweb), matching how every tag has actually
  been pushed this whole session.
- INSTALL-AIO.md / notes/deploy-nc01.md: the deploy snippet did
  `git archive main` unconditionally, silently deploying the main
  branch even if an operator had checked out a specific tag or
  maintenance branch as instructed elsewhere. Changed to `git archive
  HEAD`.
- README.md: the compatibility matrix's rollback advice
  ("git checkout v0.3.4") doesn't actually work as a rollback anymore
  post-rename - that tag still declares the old occweb id, so checking
  it out under an extended_occweb-named install just breaks. Added a
  caveat explaining what a real rollback requires; full migration docs
  are still deferred to when a test instance exists (tracked
  separately, not written up here).
2026-09-26 16:48:58 +02:00
dillard a5f5c5cdb7 Fix code-review findings: ajax error handling, translated comment, docs
- js/index.js: the occ-command ajax call used the wrong jQuery .fail()
  signature ((response, code) instead of (jqXHR, textStatus,
  errorThrown)), so any failure printed the literal string
  "[object Object]" instead of a useful message.
- js/index.js: the initial bootstrap call that builds the terminal had
  no .fail() handler at all - a failure there (expired session/CSRF
  token, network error, rejected admin check) left #app-content
  permanently blank with zero diagnostic information. This is exactly
  what made today's NC34 debugging session harder than it needed to
  be; now it shows the actual HTTP status/reason in the page.
- OccController.php: translated a leftover Dutch comment (missed by
  the earlier Russian->English pass since it wasn't Russian), and
  documented why requireAdmin() is intentional defense-in-depth rather
  than a misunderstanding of the framework: verified against
  Nextcloud's own SecurityMiddleware source that any controller method
  without @NoAdminRequired already rejects non-admins by default, so
  this check (and the original "security fix" commit that added it)
  was addressing a non-issue for THIS controller specifically.
  DbController's equivalent check is still genuinely load-bearing -
  that controller originally shipped with @PublicPage, which does
  bypass the default protection - so it gets a note explaining the
  distinction instead.
- README: added compatibility matrix rows for 0.4.0 (app id rename to
  extended_occweb) and 0.4.1 (these fixes), both verified on NC 34.0.4.

Deferred to when a test instance exists (tracked, not fixed here): no
documented migration path from the old occweb id to extended_occweb
for existing installs, OccController's constructor doing expensive
work before the (redundant) admin check runs, and js/jquery.js loading
unconditionally on NC30-33 where core still provides jQuery.

Version bump to 0.4.1.
v0.4.1-nc34
2026-09-26 16:04:14 +02:00
dillard 221ea8a249 Shorten navigation menu label to "OCC Web" (was "OCCWeb")
Full app name stays "Extended OCC Web" for the App Store/apps
management page - only the top-nav menu entry needed a shorter,
readable label.
2026-09-26 15:55:10 +02:00
dillard 138ee88486 Rename app id from occweb to extended_occweb, prep for App Store
Nextcloud's app id "occweb" is effectively unavailable for a new store
listing: the original Adphi/occweb still owns that App Store slot
(stale, v0.2.3), and el-choco/occweb_v2 has since published a
same-lineage rewrite under its own id. Rather than fight over the
"occweb" name, publish this fork under its own id, matching the
<name> this app has always had: "Extended OCC Web".

Renames every place the literal app id is used: info.xml <id> and the
auto-generated navigation <route>, script()/style() registrations in
templates/index.php, the TemplateResponse app name in
OccController::index(), the /apps/... base URL in js/index.js, and the
'app' log context tag in DbController.php. The GitHub repo name and
its <website>/<bugs>/<repository>/<screenshot> links are unaffected -
those identify the repo, not the Nextcloud app.

Docs (README, INSTALL-AIO.md, install.sh) updated to clone into and
reference an "extended_occweb" directory, since Nextcloud requires the
app folder name to match its id - a plain `git clone` of this repo no
longer produces a correctly-named install directory on its own.

Version bump to 0.4.0 given the scope (breaking change for anyone who
already deployed under the old "occweb" id - requires disable+remove
of the old app and a fresh enable under the new id, not an in-place
upgrade).
2026-09-26 12:51:21 +02:00
dillard f091cc8c0b Add NC-version compatibility matrix to README v0.3.5-nc34 2026-09-26 12:27:49 +02:00
dillard 2872d70653 Fix NC34 compatibility: vendor jQuery, send CSRF token explicitly on ajax calls
Live-tested against nc01 after its auto-update jumped to NC 34.0.4:

- Nextcloud's global jQuery has been deprecated since NC19 and is no
  longer reliably present/first-loaded on NC34 (other apps like
  serverinfo hit the same issue). occweb's index.js and the vendored
  jquery.terminal (term.js) both assume a global jQuery, so ship our
  own copy (js/jquery.js, 3.7.1 from code.jquery.com) and load it
  before them in templates/index.php. Fixes a hard "jQuery is not
  defined" crash that left the terminal blank (black screen).

- With that fixed, every ajax call started failing with HTTP 412
  "CSRF check failed": core only auto-attaches the requesttoken
  header to requests made through ITS OWN globally-loaded jQuery, and
  with occweb now vendoring a separate jQuery instance that no longer
  happens. A global $.ajaxSetup() call turned out unreliable (another
  app's later jQuery load can replace window.jQuery afterwards), so
  each of the three ajax calls (SQL query, initial completion list,
  occ command execution) now sets the requesttoken header explicitly
  via OC.requestToken.

Bump max-version to 34 - actually verified working on NC 34.0.4 now,
not inherited/guessed (see the policy comment in this same file).
Version bump to 0.3.5.
2026-09-26 12:23:28 +02:00
dillard 1a333d161b Replace INSTALL-AIO.md with a generic, English, host-agnostic version
The previous version was in Dutch and hardcoded internal deploy details
(hostname alias, personal local path, private Gitea mirror domain) that
had no business being in a public repo. Replaced with a generic version
using <docker-host> placeholders; the internal nc01-specific copy now
lives at notes/deploy-nc01.md, which is gitignored and stays local-only.

Not rewriting prior history for this (confirmed with user: nothing
credential-level was exposed, just a hostname alias and a path).
2026-09-26 03:59:00 +02:00
dillard 827b2d10dd Revert max-version to 33: only claim what we've actually verified
35 was fanategorius's own tested value, but this is now a modified
fork (CSRF fix, admin checks, translated internals) - inheriting their
compatibility claim isn't the same as having verified it ourselves.
33 is the only version this fork has actually run on (nc01).

An untested max-version that's too high is worse than one that's
accurate: on the next core bump, Nextcloud's own compat check would
keep the app enabled on an unverified major instead of cleanly
disabling it, so a real incompatibility would surface as a runtime
error instead of a safe, expected "needs testing" disable.

Policy going forward (documented inline in info.xml): test against
the next major's beta ahead of time and bump max-version before that
major goes final, so the app is never caught by surprise - never bump
it reactively/speculatively.
2026-09-26 03:53:16 +02:00
dillard 20bdb7dcd6 Fix install/clone instructions to point at our fork, revert max-version to 35
README.md and install.sh's install instructions still told new users to
git clone the abandoned upstream (fanategorius/occweb), which lacks all
of this fork's fixes. Point those at dillardblom/occweb; leave the
Credits section's links to Adphi/fanategorius as-is since those are
deliberate attribution, not install instructions. Also repoint the
README screenshot badge, which was hotlinking Adphi's repo.

composer.json still listed only the original author; add ourselves
alongside, matching the info.xml author field.

Revert max-version 40->35: 35 was fanategorius's own tested release
value (commit 7c09dd2), not a guess. Bumping to 40 was our own
speculative headroom into an untested future major - exactly the
pattern this project's own history already reverted twice, for the
same stated reason ("hasn't been tested against newer Nextcloud
releases"). Getting auto-disabled by the compat check on an untested
major is the correct, safe failure mode; claiming compatibility we
haven't verified is not. 35 still satisfies the Codex finding (no
narrowing below a previously-validated ceiling) without guessing ahead.
2026-09-26 03:50:37 +02:00
dillard c2186ce2ea Give max-version real headroom instead of pinning to nc01's exact core version
Codex review flagged the previous 35->33 change as an unjustified
compatibility regression: nothing in this app's code is actually
incompatible with NC 34/35, so narrowing the ceiling only reflects
nc01's current version, not a real limitation. Worse, nc01's AIO now
auto-updates its core nightly (watchtower-aio), so pinning max-version
to today's exact version would very likely repeat the 2026-08-09
incident (occweb auto-disabled by the compat check) on the very next
core bump. This app only calls a handful of stable, long-lived OCP
interfaces, so a generous ceiling carries little real compatibility
risk compared to that recurring outage.
2026-09-26 03:38:48 +02:00
dillard f4395d8dda Add INSTALL-AIO.md to the repo, rewrite it for the actual AIO/custom_apps workflow
Was untracked locally and stale (referenced apps/ instead of custom_apps/,
git clone inside the container instead of our tar-based deploy). Documents
the two things that just caused a production incident on nc01: files
extracted from a tarball keep the source uid/gid and need an explicit
chown to www-data, and skipping the installed_version sync after bumping
<version> puts the whole instance in "needs upgrade" state, which blocks
not just occ commands but the web login page too.

Link it from README.md's install section.
2026-09-26 03:27:27 +02:00
dillard 97ceaf7c37 Point website/bugs/repository/screenshot at our own fork
Upstream (fanategorius/occweb) is abandoned; this fork is the
maintained one now, so app metadata should point here instead.
v0.3.4
2026-09-26 02:01:29 +02:00
dillard 292b8980cb Update app author name to maintainer's own name 2026-09-26 02:00:01 +02:00
dillard faa389c4b1 Update app author email to maintainer's own address
Upstream (fanategorius/occweb) is abandoned; this fork is maintained
independently, so the contact address in info.xml should point here.
2026-09-26 01:59:27 +02:00
dillard 2af32728f7 Correct max-version to 33 (actual nc01 core version, not 36)
occ status on nc01 reports 33.0.8.2, not 36 as assumed — match the
repo's established convention of pinning max-version to the actually
tested/installed core version rather than a guess.
2026-09-26 01:55:56 +02:00
dillard ad0ce047e5 Remove unjustified @NoCSRFRequired from DbController::query(), bump max-version to 36
query() runs arbitrary admin SQL (including DELETE/UPDATE) and was
exempted from CSRF protection — a leftover from the original upstream
code, which had this alongside @PublicPage (no auth at all) before our
admin-check hardening. The client already goes through the standard
$.ajax() flow that auto-attaches Nextcloud's CSRF token, exactly like
OccController::cmd() does without any such exemption, so removing it
has no functional cost.

Also bump nextcloud max-version to 36 to match nc01's current core
version, now that AIO auto-updates the core nightly.

Version bump to 0.3.4.
2026-09-26 00:36:16 +02:00
dillard 7d05643ee6 Translate Russian comments and user-facing strings to English
Covers OccOutput.php, DbController.php and js/index.js: docblocks,
inline comments, the forbidden-construct labels shown to admins in
error responses, and the SQL template descriptions shown in the
terminal (delete-user, list-user, rename-user).
2026-09-26 00:13:03 +02:00
dillard 040743d351 Credit fanategorius and Git-Usr123 forks in README 2026-09-11 17:52:59 +02:00
dillard 85890e12db Update README to reflect active fork status, drop stale Russian translation
Also backport OccController's getConfig() -> IConfig::class fix (deprecated
internal API -> stable OCP interface), spotted while comparing against
Git-Usr123/occweb.
2026-09-11 17:40:12 +02:00
dillard cd01a2fa17 Merge branch 'fix/fetch-return-type-php81' into deploy-nc01 2026-08-09 13:06:32 +02:00
dillard a4a8f9a4eb Fix missing admin check on OccController routes (security)
index(), cmd() and list() were accessible to any logged-in user.
The navigation menu already restricts visibility to admins, but the
routes themselves were unprotected — any authenticated user who knows
the URL could execute occ commands or list all available commands.

Fix: inject IGroupManager + IUserSession, add requireAdmin() helper,
call it at the start of all three public methods. Matches the pattern
already used in DbController.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 22:04:05 +02:00
dillard f681caca35 Fix OccOutput::fetch() return type for PHP 8.1+ compatibility
BufferedOutput::fetch() declares a string return type in Symfony 5+.
PHP 8.1 enforces return type compatibility with parent methods, causing
a fatal 500 error when the occweb page loads.

Fix: add ': string' return type to OccOutput::fetch().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 21:25:56 +02:00
Egor Bugaev 9e15c3b96c Add rename-user SQL template; make batch transaction handling robust against non-Postgres backends
- New 'rename-user' template (templates / template rename-user <old> <new>):
  best-effort uid rename across core tables (oc_users, oc_preferences,
  oc_group_user, oc_group_admin, oc_ldap_user_mapping, oc_share, oc_mounts,
  oc_storages). Explicitly NOT a supported Nextcloud operation - the
  generated script carries an in-line warning (as leading SQL comments)
  that app-specific tables (Talk, Calendar, Contacts, Mail, 2FA/WebAuthn...)
  are not covered and that the data directory must be renamed on disk
  manually, followed by occ files:scan --all. Documented the same caveat
  in both READMEs.
- Hardened the round-2 transaction wrapping: beginTransaction()/commit()/
  rollBack() are now wrapped in try/catch. PostgreSQL (our backend) has
  fully transactional DDL so this wasn't actually broken here, but
  Nextcloud also supports MySQL/MariaDB via the same IDBConnection, where
  DDL implicitly commits - on that backend a DDL statement in the batch
  would make a later commit()/rollBack() throw 'no active transaction'
  and previously that exception was unhandled (HTTP 500 instead of a
  clean JSON response). Now: if rollBack() itself fails after an error,
  we report rollbackFailed+warning instead of falsely claiming rolledBack,
  since earlier statements in that batch may already be permanently
  applied. If commit() fails with nothing to commit (already
  auto-committed), that's logged only, since the effects are already
  durably persisted.
2026-07-06 19:52:05 +03:00
Egor Bugaev ca0605706f Security hardening round 2: comment-aware forbidden-pattern check, UPDATE confirmation, transactional batches, quote-aware split (incl. double quotes), NULL-safe table rendering, AJAX timeout, request size limit
- removeAllComments() strips -- and /* */ comments before checking
  FORBIDDEN_PATTERNS, closing a bypass where a comment inserted between
  a function name and '(' hid it from the regex (e.g. pg_read_file/**/(...)).
- UPDATE now requires confirm=true just like DELETE (deleteCount/updateCount).
- Batches run inside a single DB transaction; a failing statement rolls back
  everything already applied in that batch instead of leaving partial writes.
- splitStatements() (PHP and JS) now tracks a single quoteChar instead of a
  bool, handling both '...' strings and "..." quoted identifiers symmetrically.
- Frontend TEMPLATES no longer rely on SET/current_setting: the escaped uid
  is inlined into every statement, so partial copy/paste still works.
- renderTable() renders NULL as [NULL] instead of an empty string, so it's
  no longer visually identical to an empty string value.
- Added MAX_SQL_BYTES (1MB) request size limit, returned as HTTP 413.
- Added a 120s client-side AJAX timeout with a distinct timeout message,
  so a hung request doesn't leave the terminal paused indefinitely.
2026-07-06 19:38:25 +03:00
Egor Bugaev 341165eb38 Add post-clone install.sh (cleanup + chown + occ app:enable), document it in both READMEs 2026-07-06 19:15:09 +03:00
Egor Bugaev e069fdeb52 Expand .gitignore: vendor, node_modules, PHPUnit cache, OS/IDE junk 2026-07-06 19:05:48 +03:00
Egor Bugaev 5c5aa3ec6a Document install-on-a-new-server steps and installed_version sync gotcha 2026-07-06 19:02:30 +03:00
fanategorius 7c09dd2278 Update info.xml 2026-07-06 18:57:32 +03:00
Egor Bugaev 6217ec5405 Pin max-version back to 31 (matches actual installed Nextcloud) to keep update banner suppressed 2026-07-06 18:29:12 +03:00
Egor Bugaev c49bebc73e Set max-version to 50 so occweb doesn't block future Nextcloud update visibility 2026-07-06 18:16:11 +03:00