{"record":{"id":"c77551b973b69ac5","repo":"Dolibarr/dolibarr","slug":"bad-link-file-is-from-another-module-part","errorCode":null,"errorMessage":"Bad link. File is from another module part.","messagePattern":"Bad link\\. File is from another module part\\.","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"htdocs/document.php","lineNumber":224,"sourceCode":"\t} else {\n\t\tinclude_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';\n\t\t$ecmfile = new EcmFiles($db);\n\t\t$result = $ecmfile->fetch(0, '', '', '', $hashp);\n\t\tif ($result > 0) {\n\t\t\t$tmp = explode('/', $ecmfile->filepath, 2); // $ecmfile->filepath is relative to document directory\n\t\t\t// filepath can be 'users/X' or 'X/propale/PR11111'\n\t\t\tif (is_numeric($tmp[0])) { // If first tmp is numeric, it is subdir of company for multicompany, we take next part.\n\t\t\t\t$tmp = explode('/', $tmp[1], 2);\n\t\t\t}\n\t\t\t$moduleparttocheck = $tmp[0]; // moduleparttocheck is first part of path\n\n\t\t\tif ($modulepart) {    // Not required, so often not defined, for link using public hashp parameter.\n\t\t\t\tif ($moduleparttocheck == $modulepart) {\n\t\t\t\t\t// We remove first level of directory\n\t\t\t\t\t$original_file = (($tmp[1] ? $tmp[1] . '/' : '') . $ecmfile->filename); // this is relative to module dir\n\t\t\t\t\t//var_dump($original_file); exit;\n\t\t\t\t} else {\n\t\t\t\t\thttponly_accessforbidden('Bad link. File is from another module part.', 403);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$modulepart = $moduleparttocheck;\n\t\t\t\t$original_file = (($tmp[1] ? $tmp[1] . '/' : '') . $ecmfile->filename); // this is relative to module dir\n\t\t\t}\n\n\t\t\t$entity = $ecmfile->entity;\n\t\t\tif (isModEnabled('multicompany') && !empty($ecmfile->src_object_type) && $ecmfile->src_object_id > 0) {\n\t\t\t\t$object = fetchObjectByElement($ecmfile->src_object_id, $ecmfile->src_object_type);\n\t\t\t\tif (is_object($object) && $object->id > 0) {\n\t\t\t\t\t$entity = $object->entity;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($entity != $conf->entity) {\n\t\t\t\t$conf->entity = $entity;\n\t\t\t\t$conf->setValues($db);\n\t\t\t\t// Multicompany: Here we are switching entity and later we will check the requested object is in this entity but may be that user is not allowed to log/see entity","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/document.php#L206-L242","documentation":"When a file is fetched via shared hash, document.php derives a moduleparttocheck from the stored ECM record and compares it to the modulepart supplied in the URL. If a modulepart is given and it does not match the record's module, access is forbidden with HTTP 403 — preventing a hash for one module's file being combined with another module's path.","triggerScenarios":"document.php called with both hashp (or ecmfile lookup) and a modulepart that differs from the module stored on the ECM file record, e.g. modulepart=invoice for a file stored under 'expensereport' — the `$moduleparttocheck != $modulepart` branch fires.","commonSituations":"Manually edited share URLs where modulepart was changed; links generated for one module then reused for files of another; module renamed between link creation and click (e.g. fckeditor→medias mismatches).","solutions":["Use the URL exactly as generated by Dolibarr — do not alter modulepart","Omit modulepart for hashp-based links (it is optional for shared links); Dolibarr will infer it from the record","Regenerate the link from the correct module's document list","Check for module renames/custom modulepart mismatches between stored ecm record and URL"],"exampleFix":"// before\ndocument.php?modulepart=facture&hashp=<hash of an ecm file owned by 'ecm'>\n// after\ndocument.php?hashp=<hash>","handlingStrategy":"validation","validationCode":"// ensure URL modulepart matches the ecm record before building link\nif (!empty($modulepart) && $modulepart !== $ecmfile->src_object_type_module) {\n  throw new Exception('modulepart mismatch with file owner module');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit modulepart in share URLs","For hashp links, omit modulepart and let Dolibarr infer it","Keep modulepart values consistent across module renames","Test share links after enabling/renaming external modules"],"tags":["http-403","download","security"],"backgroundTag":"permission-denied","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"}