Skip to content

Port: string identifiers read as foreign keys, and php-fpm error log ownership - #1167

Merged
mastacontrola merged 2 commits into
dev-branchfrom
port-notint-dev
Aug 18, 2026
Merged

Port: string identifiers read as foreign keys, and php-fpm error log ownership#1167
mastacontrola merged 2 commits into
dev-branchfrom
port-notint-dev

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

Ports the two of today's working-1.6 fixes that this branch actually has.

1. String identifiers read as foreign keys — #1153 + #1162

FOGController infers "this is an integer id" from the key's name ending in
id. Right for every real foreign key in the tree, wrong for a string
identifier spelled the same way. The inference lives in two methods,
save() and isValid(), each with its own copy — which is the trap: fixing
only save() leaves an object that saves and then fails validation forever
after. Both are fixed here, in one commit, because splitting them serves nobody
on a maintenance branch.

The casualty list is exhaustive, not "the ones that got noticed"

Every *id key every model declares was resolved to its (table, column) pair
and typed against a real 1.5.10 install's information_schema; all 38
plugin *id columns were typed from their own Schema::createTable() lists.
Exactly two are not integers:

Model key Column Type Effect Fix here
inventory.sysuuid iSystemUUID varchar(255) SMBIOS system UUID written as 0 on every inventory write declared not-int
taskLog.taskID taskID mediumtext a non-numeric value written as 0 declared not-int

Reproduced and fixed on a real 1.5 server

Shadow tree in ~ on the 1.5 lab box, running against its own database — the
live tree was never touched (grep for the guard on /var/www/html/fog after:
0):

=== UNPATCHED (live 1.5 code) ===
wrote id=2 sysuuid sent=4C4C4544-0031-3010-8039-CAC04F565831 stored='0'  LOST
=== PATCHED (this port) ===
wrote id=3 sysuuid sent=4C4C4544-0031-3010-8039-CAC04F565831 stored='4C4C4544-0031-3010-8039-CAC04F565831'  MATCH

Both probe rows were destroyed; inventory is back to 0 rows.

taskLog.taskID is fixed differently from 1.6, on purpose

1.6 fixed the column (schema 336, #1156). A column type change is a data
migration, and a maintenance branch is the wrong place to run one over a table
that can hold every task a server has ever run. Declaring the field does the
same job with no schema change. The comment in tasklog.class.php says so, and
points at #1156.

2. php-fpm's error log — #1165

Same defect here: the installer rewrites the pool to run as $apacheuser and
leaves the packaged log owned by the distro's user, so the pool cannot open the
file named by its own php_admin_value[error_log] and every error_log()
call from FOG's PHP is discarded
. The log sits at zero bytes looking like an
install with no errors, and nothing reports it.

Guarded exactly like the session-path chown directly above it; the directory
is only re-owned when its own basename marks it as php-fpm's, because on Debian
the log sits directly in /var/log.

Not ported: #1163

The nginx query-string fix does not apply to this branch, and the check was not
"it looks similar":

  • the installer writes no nginx vhost at all here — zero location ~ and
    zero try_files in lib/common/functions.sh; only the Apache rewrite, which
    carries [QSA,L] and was never affected;
  • Route reads no query parameters here — no INPUT_GET, no
    QUERY_STRING, no $_GET[, and no queryParam() to make public;
  • there is no OIDC plugin on this branch to be broken by it.

Test

tests/databasefields-notint.test.php pins both methods separately, including
that each builds its own lookup before the branch that reads it, plus the two
casualties by name. It deliberately carries no column-type sweep: 1.6's
version reads commons/schema-expected.php, which this branch does not have,
and deriving types by regex from schema.php's accumulated ALTER statements
gives wrong answers — it matched an msState belonging to a different table
when tried. The sweep was done once, properly, against a live schema; the test
pins its findings.

Eight mutations verified, all caught, including a comment-only one.
sh tests/run-all.sh → 8 passed, 0 failed.

🤖 Generated with Claude Code

https://claude.ai/code/session_017aBSWrDArXHTpKWkkN27LR

mastacontrola and others added 2 commits August 17, 2026 21:04
Port of working-1.6 GH-1153 and GH-1162, which landed there separately.
Both halves are here in one commit because splitting them serves nobody
on a maintenance branch: the first alone leaves an object that saves and
then fails validation forever after.

FOGController infers "this is an integer id" from the key's name ending
in "id". Right for every real foreign key in the tree, wrong for a
string identifier spelled the same way, and the two failure modes
differ. Required: throws "Required database field is empty: <key>" about
a field that is filled in. Optional: filter_var fails, the value becomes
0, and the real one is lost with no error anywhere.

The inference lives in TWO methods, save() and isValid(), each with its
own copy. $databaseFieldsNotInt lets a model say which of its *id keys
is a string, and both methods now honor it -- the name is a proxy for
the column's type, and the model is the only thing that knows the type.

Two casualties on this branch, and the list is exhaustive rather than
the ones that got noticed: every *id key every model declares was
resolved to its (table, column) pair and typed against a real 1.5.10
install's information_schema, and all 38 plugin *id columns were typed
from their own Schema::createTable() lists. Everything else is an
integer.

  inventory.sysuuid -> iSystemUUID, varchar(255). The SMBIOS system UUID
  was written as 0 on every inventory write. Reproduced on the 1.5 lab
  server against its own database: sent
  4C4C4544-0031-3010-8039-CAC04F565831, stored '0'. With this commit
  overlaid, the same write stores the UUID. The probe row was destroyed
  and the live tree was never touched.

  taskLog.taskID, mediumtext. 1.6 fixed the column instead (schema 336,
  GH-1156). A column type change is a data migration, and a maintenance
  branch is the wrong place to run one over a table that can hold every
  task a server has ever run -- so this does the same job without
  touching the schema.

tests/databasefields-notint.test.php pins both methods separately,
including that each builds its own lookup before the branch that reads
it. It deliberately carries no column-type sweep: 1.6's version reads
commons/schema-expected.php, which this branch does not have, and
deriving types by regex from schema.php's accumulated ALTER statements
gives wrong answers -- it matched an msState belonging to a different
table when tried. The sweep was done once, properly, against a live
schema; the test pins its findings by name. Eight mutations verified,
all caught, including a comment-only one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aBSWrDArXHTpKWkkN27LR
Port of working-1.6 GH-1165. This branch has the same defect: it
rewrites the php-fpm pool to run as $apacheuser and leaves the packaged
log owned by the distro's user.

The pool then cannot open the file named by its own
php_admin_value[error_log], so every error_log() call from FOG's PHP is
discarded. It fails more quietly than the session directory the block
above already re-owns: nothing reports it, and the log sits at zero
bytes looking like an install with no errors. Measured on a Fedora nginx
install -- /var/log/php-fpm owned apache:root, www-error.log owned
apache:apache, pool running as nginx, `test -w` refusing both.

The file is re-owned unconditionally and created first if the pool has
never managed to write it. The directory only when its own basename
marks it as php-fpm's: on Debian the log sits directly in /var/log, and
chowning that to the web user would be a far worse bug than this one.
Both parses are guarded the way the session-path chown above is --
absolute, not "/", parent must exist -- so "syslog", or a commented-out
line, resolves to nothing rather than to a chown of somewhere that
matters.

Simulated against both layouts and three refusal cases before it went
in, and verified on the 1.6 server by writing through the pool user
afterwards. logrotate keeps the ownership: the packaged php-fpm rule
carries no `create` line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aBSWrDArXHTpKWkkN27LR
@mastacontrola
mastacontrola merged commit eff3f5f into dev-branch Aug 18, 2026
4 of 6 checks passed
@mastacontrola
mastacontrola deleted the port-notint-dev branch August 18, 2026 02:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant