{"record":{"id":"98353073a8834644","repo":"parallax/jsPDF","slug":"invalid-permission-perm","errorCode":null,"errorMessage":"Invalid permission: \" + perm","messagePattern":"Invalid permission: \" \\+ perm","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/libs/pdfsecurity.js","lineNumber":44,"sourceCode":" * @name constructor\r\n * @function\r\n * @param {Array} permissions Permissions allowed for user, \"print\", \"modify\", \"copy\" and \"annot-forms\".\r\n * @param {String} userPassword Permissions apply to this user. Leaving this empty means the document\r\n *                              is not password protected but viewer has the above permissions.\r\n * @param {String} ownerPassword Owner has full functionalities to the file.\r\n * @param {String} fileId As hex string, should be same as the file ID in the trailer.\r\n * @example\r\n * var security = new PDFSecurity([\"print\"])\r\n */\r\nfunction PDFSecurity(permissions, userPassword, ownerPassword, fileId) {\r\n  this.v = 1; // algorithm 1, future work can add in more recent encryption schemes\r\n  this.r = 2; // revision 2\r\n\r\n  // set flags for what functionalities the user can access\r\n  let protection = 192;\r\n  permissions.forEach(function(perm) {\r\n    if (typeof permissionOptions.perm !== \"undefined\") {\r\n      throw new Error(\"Invalid permission: \" + perm);\r\n    }\r\n    protection += permissionOptions[perm];\r\n  });\r\n\r\n  // padding is used to pad the passwords to 32 bytes, also is hashed and stored in the final PDF\r\n  this.padding =\r\n    \"\\x28\\xBF\\x4E\\x5E\\x4E\\x75\\x8A\\x41\\x64\\x00\\x4E\\x56\\xFF\\xFA\\x01\\x08\" +\r\n    \"\\x2E\\x2E\\x00\\xB6\\xD0\\x68\\x3E\\x80\\x2F\\x0C\\xA9\\xFE\\x64\\x53\\x69\\x7A\";\r\n  let paddedUserPassword = (userPassword + this.padding).substr(0, 32);\r\n  let paddedOwnerPassword = (ownerPassword + this.padding).substr(0, 32);\r\n\r\n  this.O = this.processOwnerPassword(paddedUserPassword, paddedOwnerPassword);\r\n  this.P = -((protection ^ 255) + 1);\r\n  this.encryptionKey = md5Bin(\r\n    paddedUserPassword +\r\n      this.O +\r\n      this.lsbFirstWord(this.P) +\r\n      this.hexToBytes(fileId)\r","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/parallax/jsPDF/blob/a3930ce03a585a26b2c76d12a0f413ce96f6d1a3/src/libs/pdfsecurity.js#L26-L62","documentation":"Intended to fire inside PDFSecurity when a permission string passed in the permissions array is not one of the recognized keys (print, modify, copy, annot-forms). NOTE: the guard is buggy -- it checks `typeof permissionOptions.perm` (a literal property 'perm' that never exists) instead of `permissionOptions[perm]`, so this branch is effectively dead code and the throw is unreachable. An invalid permission instead silently makes protection become NaN via `protection += permissionOptions[perm]` (undefined), corrupting the P flag without any error.","triggerScenarios":"Constructing `new PDFSecurity(permissions, userPassword, ownerPassword, fileId)` with a typo'd or unknown permission such as 'editing', 'annotations', 'extract'. Because of the bug you will NOT see this error; you will get a broken /P value. This analysis describes the intended trigger.","commonSituations":"Enabling PDF encryption via jsPDF's encryption option and passing a wrong permission label; copying permission names from another library's API (e.g. pdfkit uses different strings).","solutions":["Restrict the permissions array to exactly the four valid keys: 'print', 'modify', 'copy', 'annot-forms'.","Validate/whitelist the array before constructing PDFSecurity and drop unknown entries.","Be aware the library's own guard does not catch typos (bug), so do not rely on it -- add your own check."],"exampleFix":"// before\nnew PDFSecurity(['print', 'editing'], userPwd, ownerPwd, fileId); // 'editing' invalid\n\n// after\nvar ALLOWED = ['print', 'modify', 'copy', 'annot-forms'];\nvar perms = ['print', 'editing'].filter(function(p){ return ALLOWED.indexOf(p) !== -1; });\nnew PDFSecurity(perms, userPwd, ownerPwd, fileId);","handlingStrategy":"validation","validationCode":"var ALLOWED = ['print', 'modify', 'copy', 'annot-forms'];\nvar valid = permissions.filter(function(p){ return ALLOWED.indexOf(p) !== -1; });\nif (valid.length !== permissions.length) {\n  throw new Error('Unknown permission in ' + JSON.stringify(permissions));\n}\nnew PDFSecurity(valid, userPassword, ownerPassword, fileId);","typeGuard":"function isValidPermission(p) {\n  return ['print','modify','copy','annot-forms'].indexOf(p) !== -1;\n}","tryCatchPattern":"try {\n  security = new PDFSecurity(permissions, userPwd, ownerPwd, fileId);\n  if (isNaN(security.P)) throw new Error('Permissions produced invalid P flag (bad permission string)');\n} catch (e) {\n  if (/Invalid permission/.test(e.message)) { /* filter and retry */ }\n  else throw e;\n}","preventionTips":["Whitelist permissions to the four valid keys before constructing PDFSecurity.","Do not rely on the library's guard -- it is buggy and will not catch typos; add your own.","After construction, verify security.P is a finite number to detect silent NaN corruption."],"tags":["pdf","encryption","security","validation","bug"],"backgroundTag":null,"analyzedSha":"a3930ce03a585a26b2c76d12a0f413ce96f6d1a3","analyzedAt":"2026-08-13T05:33:39.648Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}