Skip to content

Commit b19badc

Browse files
committed
fix(nextcloud): invoke occ via php to not require executable bit
run_occ() now resolves `php` via shutil.which() and calls `sudo -u \#<uid> php <occ> <cmd>` instead of relying on `occ` itself being executable or its shebang resolving to a working interpreter. Returns a clear error if no php is found in PATH.
1 parent dd9d79c commit b19badc

2 files changed

Lines changed: 22 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## [Unreleased]
1010

11+
### Changed
12+
13+
* nextcloud.py: `run_occ()` no longer relies on the Nextcloud `occ` script being marked executable. It now locates `php` via `shutil.which('php')` and invokes `sudo -u \#<uid> php <occ> <cmd>`, which also works on installations where `occ` lacks the execute bit or its shebang does not resolve to a working PHP interpreter. If no `php` is found in `PATH`, the call returns a descriptive error instead of silently failing
14+
15+
1116
### Security
1217

1318
* Harden the CI supply chain: the `pre-commit` install in the pre-commit-autoupdate workflow is now hash-pinned via `.github/pre-commit/requirements.txt` (generated with `pip-compile --generate-hashes --strip-extras`), and `dependabot/fetch-metadata` is pinned to a commit SHA so all GitHub Actions used in `.github/workflows/` are now pinned by hash. The policy is documented in CONTRIBUTING.md under "CI Supply Chain"

nextcloud.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
"""This library collects some Nextcloud related functions."""
1212

1313
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
14-
__version__ = '2025100301'
14+
__version__ = '2026041401'
1515

1616
import json
1717
import os
18+
import shutil
1819

1920
from . import disk, shell
2021

@@ -23,9 +24,11 @@ def run_occ(path, cmd, _format='json'):
2324
"""
2425
Run a Nextcloud `occ` command as the owner of `config/config.php`.
2526
26-
The function determines the UID owning `config/config.php` inside the given
27-
Nextcloud installation, then executes `occ` via `sudo -u` using that UID.
28-
By default it parses JSON output if requested.
27+
The function locates the PHP interpreter on the system and invokes `occ` explicitly as
28+
`php <occ> <cmd>`, running via `sudo -u` under the numeric UID that owns
29+
`config/config.php`. Calling PHP directly avoids relying on `occ` being marked executable
30+
or on its shebang resolving to a working interpreter, which is not always the case on
31+
hardened or SCL-based installations.
2932
3033
### Parameters
3134
- **path** *(str | os.PathLike)*:
@@ -45,6 +48,8 @@ def run_occ(path, cmd, _format='json'):
4548
message.
4649
4750
### Notes
51+
- PHP is resolved via `shutil.which('php')`. If no `php` binary is found in `PATH`, the
52+
call fails with a descriptive error.
4853
- Requires passwordless or otherwise configured `sudo` permissions for `sudo -u <uid>` to
4954
succeed.
5055
- The command runs as the numeric UID of `config/config.php`’s owner, not by username.
@@ -65,12 +70,19 @@ def run_occ(path, cmd, _format='json'):
6570
>>> ok in (True, False)
6671
True
6772
"""
73+
php = shutil.which('php')
74+
if not php:
75+
return False, (
76+
'Could not find a `php` interpreter in PATH. Install PHP or make sure it is '
77+
'reachable for the user running the plugin.'
78+
)
79+
6880
# get the owner of config.php
6981
user = disk.get_owner(os.path.join(path, 'config/config.php'))
7082
occ = os.path.join(path, 'occ')
7183
# When running a command as a UID, many shells require
7284
# that the `#` be escaped with a backslash (`\`).
73-
sudo_cmd = f'sudo -u \\#{user} {occ} {cmd}'
85+
sudo_cmd = f'sudo -u \\#{user} {php} {occ} {cmd}'
7486

7587
success, result = shell.shell_exec(sudo_cmd)
7688
stdout, stderr, rc = result

0 commit comments

Comments
 (0)