Skip to content

UploadClientMimeTrusted ​

warning native on by default

Group: Security

Detect acceptance of an upload based solely on $_FILES entry type compared to an allowed MIME string without server-side content inspection.

Example ​

php
<?php
if ($_FILES['file']['type'] === 'image/png') { move_uploaded_file($_FILES['file']['tmp_name'], '/srv/uploads/image.png'); }
function storeAvatar() {
    if (!in_array($_FILES['avatar']['type'], ['image/png', 'image/jpeg'], true)) { return; }
    move_uploaded_file($_FILES['avatar']['tmp_name'], '/srv/avatars/new');
}
function storeScan() {
    if ($_FILES['scan']['type'] !== 'image/png') { return; }
    if (getimagesize($_FILES['scan']['tmp_name']) === false) { return; } // content inspected: no report
    move_uploaded_file($_FILES['scan']['tmp_name'], '/srv/scans/new');
}

Reported:

  • line 2: Inspect uploaded content instead of trusting its client MIME label.
  • line 5: Inspect uploaded content instead of trusting its client MIME label.

Detection ​

  • Report a builtin move_uploaded_file() call whose source is $_FILES[key]['tmp_name'] (literal key) when the call is reached only if the client-supplied $_FILES[key]['type'] of the same entry matched an allowed MIME string, and no server-side content inspection of that temporary file is found (E1).
  • A client type test is $_FILES[key]['type'] (a literal key, or a local holding one that stays unchanged, as does every $_FILES element, up to the call) compared (==, ===, !=, !==, either operand order) with a string literal, or a builtin in_array() call (positional or named arguments) whose needle is that entry type and whose haystack is a non-empty literal array of string literals, searched strictly or, when no value reads as a number, loosely.
  • The call is reached only if the type matched: the conditions guarding it limit the entry type to string literals that all contain /. Tests combine through !, parentheses, &&/and and ||/or (each side of a disjunction must limit it), and guard the call from the branch of an if/elseif/else chain, a ternary or match (true) arm, the right operand of &&/||, or code after an if whose branch for another type always leaves (return, throw, exit, break, continue), with or without other arms.
  • A call of the project between the test and the call, which may write $_FILES, a jump label between them, a write to the entry, or a test of another entry proves nothing.
  • Resolve builtin calls and named arguments before checking the contract. Project-local callees are considered only when their bodies or immutable summaries establish the required facts. The default is enabled, with positive proof required.
  • For flow-dependent conditions, require a reachable source-to-use path with no intervening invalidation. Unknown calls, ambiguous aliases, recursion or analysis-budget exhaustion cannot establish definite facts.

Exceptions (no report) ​

  • E1. Server-side content inspection: anywhere earlier in the same function (or script) the same temporary file, written as $_FILES[key]['tmp_name'] or as a local variable assigned from it, is an argument of builtin finfo_file(), mime_content_type(), getimagesize(), exif_imagetype(), an imagecreatefrom*() decoder, or a method call named file (finfo::file(); the receiver type need not be proven). The inspection does not have to dominate the move: an acceptance path that may depend on stored bytes is not provably label-only.
  • Client MIME used only for display, logging or choosing a name; a test that rejects the allowed type; a guard that does not leave; a || alternative in an accepting branch or a && in a guard (the test is then not required); a different $_FILES entry; and upload-error or size checks alone are not client type tests.
  • Unresolved targets, incomplete required facts, suppressed findings and PHP versions lacking the referenced API or language feature produce no report. User declarations shadowing builtin names are not builtin contracts.

Fix ​

No automatic fix: content types, file size limits and image decoding policy must be selected by the application.

PHP versions ​

Apply only on PHP versions supporting the referenced APIs and syntax (within PHP 5.3–8.5). Builtin behavior and argument contracts follow the configured PHP version. Extension-specific calls require resolved extension API symbols; do not treat same-named application functions as extension calls.

Options ​

None. Enable or disable this rule through normal rule configuration.

Configure ​

In custos.json:

json
{
  "rules": {
    "UploadClientMimeTrusted": {
      "enabled": false
    }
  }
}

Suppress ​

Before the statement or declaration (or the first statement of the file):

php
// @custos-ignore UploadClientMimeTrusted

Released under the MIT License. Native rules and rules modeled on Php Inspections (EA Extended); independent clean-room implementation.