{"record":{"id":"069323442c4329de","repo":"Dolibarr/dolibarr","slug":"bad-value-for-parameter-modulepart","errorCode":null,"errorMessage":"Bad value for parameter modulepart","messagePattern":"Bad value for parameter modulepart","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"htdocs/document.php","lineNumber":285,"sourceCode":"\t$type = GETPOST('type', 'alpha');\n} else {\n\t$type = dol_mimetype($original_file);\n}\n// Security: Force to octet-stream if file is a dangerous file. For example when it is a .noexe file\n// We do not force if file is a javascript to be able to get js from website module with <script src=\"\n// Note: Force whatever is $modulepart seems ok.\nif (!in_array($type, array('text/x-javascript')) && !dolIsAllowedForPreview($original_file)) {\n\t$type = 'application/octet-stream';\n}\n\n// Security: Delete string ../ or ..\\ into $original_file\n$original_file = preg_replace('/\\.\\.+/', '..', $original_file);\t// Replace '... or more' with '..'\n$original_file = str_replace('../', '/', $original_file);\n$original_file = str_replace('..\\\\', '/', $original_file);\n\n// Security check\nif (empty($modulepart)) {\n\taccessforbidden('Bad value for parameter modulepart');\n}\n\n// Check security and set return info with full path of file\n$check_access = dol_check_secure_access_document($modulepart, $original_file, (int) $entity, $user, '', 'read');\n$accessallowed              = $check_access['accessallowed'];\n$sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];\n$fullpath_original_file     = $check_access['original_file']; // $fullpath_original_file is now a full path name\n//var_dump($modulepart.' '.$entity.' '.$fullpath_original_file.' '.$original_file.' '.$accessallowed);exit;\n\nif (!empty($hashp) && $hashp != 'shared') {\n\t$accessallowed = 1; // When using hashp, link is public so we force $accessallowed\n\t$sqlprotectagainstexternals = '';\n} else {\n\t// Basic protection (against external users only)\n\tif ($user->socid > 0) {\n\t\tif ($sqlprotectagainstexternals) {\n\t\t\t$resql = $db->query($sqlprotectagainstexternals);\n\t\t\tif ($resql) {","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/document.php#L267-L303","documentation":"After sanitizing the path (removing '..' traversal), document.php requires modulepart to be non-empty before calling dol_check_secure_access_document(); otherwise accessforbidden() is raised. modulepart is mandatory here because it selects the security rules and directory mapping used to authorize the file read.","triggerScenarios":"A request reaching this later check with modulepart still empty — e.g. document.php?original_file=... with no modulepart and no hashp path taken, or modulepart stripped by intermediate processing.","commonSituations":"Custom integrations building download URLs missing modulepart; POST forms that dropped the hidden modulepart field; URLs manipulated so modulepart ends up empty.","solutions":["Always pass a valid modulepart in the URL/form (e.g. facture, produit, ecm, medias)","Regenerate links with Dolibarr helpers that include modulepart automatically","Check form/POST handling preserves the modulepart hidden field","Confirm no upstream code unsets or empties $modulepart before the check"],"exampleFix":"// before\nheader('Location: document.php?original_file='.$relative);\n// after\nheader('Location: document.php?modulepart=medias&original_file='.$relative);","handlingStrategy":"validation","validationCode":"if (empty($modulepart) || !in_array($modulepart, $allowedModuleParts, true)) {\n  http_response_code(400); exit('valid modulepart required');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass modulepart in download URLs and forms","Keep hidden modulepart inputs in POST forms intact","Build links via Dolibarr helpers rather than string concatenation","Whitelist modulepart values in link-generating code"],"tags":["accessforbidden","download","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"598aa4bdada683d17ca04b1842548821ff0eb6c6","analyzedAt":"2026-09-14T11:12:15.309Z","contentChangedAt":"2026-09-14T11:12:15.309Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}