{"record":{"id":"4bd6c902cf3d3436","repo":"Dolibarr/dolibarr","slug":"bad-link-file-is-from-another-module-part-viewimage","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":"warning","filePath":"htdocs/viewimage.php","lineNumber":234,"sourceCode":"\tinclude_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';\n\tinclude_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';\n\t$ecmfile = new EcmFiles($db);\n\t$result = $ecmfile->fetch(0, '', '', '', $hashp);\n\tif ($result > 0) {\n\t\t$tmp = explode('/', $ecmfile->filepath, 2); // $ecmfile->filepath is relative to document directory\n\t\t// filepath can be 'users/X' or 'X/propale/PR11111'\n\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$tmp = explode('/', $tmp[1], 2);\n\t\t}\n\t\t$moduleparttocheck = $tmp[0]; // moduleparttocheck is first part of path\n\n\t\tif ($modulepart) {\t// Not required, so often not defined, for link using public hashp parameter.\n\t\t\tif ($moduleparttocheck == $modulepart) {\n\t\t\t\t// We remove first level of directory\n\t\t\t\t$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir\n\t\t\t\t//var_dump($original_file); exit;\n\t\t\t} else {\n\t\t\t\thttponly_accessforbidden('Bad link. File is from another module part.', 403);\n\t\t\t}\n\t\t} else {\n\t\t\t$modulepart = $moduleparttocheck;\n\t\t\t$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir\n\t\t}\n\n\t\tif ($extname) {\n\t\t\t$original_file = getImageFileNameForSize($original_file, $extname);\n\t\t}\n\t} else {\n\t\thttponly_accessforbidden(\"ErrorFileNotFoundWithSharedLink\", 403, 1);\n\t}\n}\n\n// Define mime type\n$type = 'application/octet-stream';\nif (GETPOST('type', 'alpha')) {\n\t$type = GETPOST('type', 'alpha');","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/viewimage.php#L216-L252","documentation":"When a file is resolved via hashp, viewimage.php fetches the EcmFiles record and compares its stored modulepart ($moduleparttocheck) with the modulepart in the URL. On mismatch it returns 403 'Bad link. File is from another module part.' — preventing access to a file by pretending it belongs to a different module directory.","triggerScenarios":"Request viewimage.php?modulepart=X&hashp=<hash> where the hash resolves to a file registered under modulepart Y; e.g. a copied link whose modulepart was edited, or the file was moved/re-registered under another modulepart after the link was created.","commonSituations":"Links generated before a module refactor changed modulepart names (e.g. fckeditor -> medias); documents migrated between modules leaving stale hashp links; users sharing links and manually changing modulepart.","solutions":["Regenerate the link with the correct modulepart matching the ecm file record","Omit modulepart entirely for hashp links — the script will derive it from the record","Fix the ecm_files record if it was created with the wrong modulepart (update llx_ecm_files.src_object/modulepart)","Check the medias/fckeditor backward-compat mapping if upgrading from old links"],"exampleFix":"// before\n$url = DOL_URL_ROOT.'/viewimage.php?modulepart=fckeditor&hashp='.$hashp;\n// after\n$url = DOL_URL_ROOT.'/viewimage.php?hashp='.$hashp; // modulepart derived from record","handlingStrategy":"validation","validationCode":"$ecmfile = new EcmFiles($db); $ecmfile->fetch(0, '', $relpath);\nif (!empty($modulepart) && $ecmfile->src_object_type_ref !== $modulepart) { throw new RuntimeException('modulepart mismatch'); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["For hashp links, omit modulepart and let the server derive it","Regenerate links after module migrations/renames (e.g. fckeditor -> medias)","Keep llx_ecm_files modulepart values consistent with the files' locations","Never hand-edit modulepart in shared URLs"],"tags":["http-403","viewimage","hashp","access-control","dolibarr"],"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"}