{
  "files": [
    {
      "path": "background/background.js",
      "status": "modified",
      "rows": [
        {
          "kind": "add",
          "text": "import \"../pma-demo/background.js\";",
          "after": 1
        },
        {
          "kind": "omitted",
          "text": "Original worker continues unchanged."
        }
      ],
      "note": "All 3,794,739 original worker bytes are unchanged. The unchanged minified worker is omitted here; the full original and modified files are linked below.",
      "addedLines": 1,
      "removedLines": 0,
      "before": {
        "url": "/downloads/extension-review/background--background.js.original.txt",
        "sha256": "5f04638e3b022c0524d7c5252e44c9ec2bb3dfe87e30749fc99bbaabe7444c9d",
        "bytes": 3794739
      },
      "after": {
        "url": "/downloads/extension-review/background--background.js.modified.txt",
        "sha256": "8282880dc48f490d7f6d5666925ce2a653d76fcd284f0d30f054b2db4a5708a7",
        "bytes": 3794775
      }
    },
    {
      "path": "manifest.json",
      "status": "modified",
      "rows": [
        {
          "kind": "hunk",
          "text": "@@ -1,18 +1,15 @@"
        },
        {
          "kind": "context",
          "text": "{",
          "before": 1,
          "after": 1
        },
        {
          "kind": "remove",
          "text": "\"update_url\": \"https://clients2.google.com/service/update2/crx\",",
          "before": 2
        },
        {
          "kind": "remove",
          "text": "",
          "before": 3
        },
        {
          "kind": "remove",
          "text": "  \"manifest_version\": 3,",
          "before": 4
        },
        {
          "kind": "remove",
          "text": "  \"name\": \"Devolutions Password Manager\",",
          "before": 5
        },
        {
          "kind": "add",
          "text": "\"manifest_version\": 3,",
          "after": 2
        },
        {
          "kind": "add",
          "text": "  \"name\": \"Devolutions Password Manager (PMA demo)\",",
          "after": 3
        },
        {
          "kind": "context",
          "text": "  \"short_name\": \"Devolutions\",",
          "before": 6,
          "after": 4
        },
        {
          "kind": "context",
          "text": "  \"default_locale\": \"en_US\",",
          "before": 7,
          "after": 5
        },
        {
          "kind": "context",
          "text": "  \"description\": \"__MSG_DWLDescription__\",",
          "before": 8,
          "after": 6
        },
        {
          "kind": "remove",
          "text": "  \"key\": \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAru5UDnxOma2l02Bay+ES9CKgvmDxjjSrRqhwOUWYU7ukocxQZ1TIZtJt1r931c37FKmTQfdss2N2c5rZi2pCUgItVKl4pT3C5aixNhzPkA4kQzAR+EYP9lSpeTCn0ShtwA71FQYK1xxa4v3MbOWaWOQ3wDwymJf74Mmtz0BFWwLUXmFrXn8Nk/m5F+2EPCY0okLf/gZfLnQoZUUtWaaFTQJlkoVWDGlZhjcJLTJKOmnGkDvp6z1+OR1+LUDY/iNm/2JdwOKB9GatGR9WwUIvQcgEslXalvZPB7xxCO/FHLBMNYPUNG1UQIXahQ3yE3ZBlRMv6ybO5lwnUfEGOQuHAQIDAQAB\",",
          "before": 9
        },
        {
          "kind": "context",
          "text": "  \"version\": \"2026.2.0.5\",",
          "before": 10,
          "after": 7
        },
        {
          "kind": "context",
          "text": "  \"version_name\": \"2026.2.0.5\",",
          "before": 11,
          "after": 8
        },
        {
          "kind": "context",
          "text": "  \"minimum_chrome_version\": \"110\",",
          "before": 12,
          "after": 9
        },
        {
          "kind": "context",
          "text": "  \"action\": {",
          "before": 13,
          "after": 10
        },
        {
          "kind": "context",
          "text": "    \"default_popup\": \"popup/index.html\",",
          "before": 14,
          "after": 11
        },
        {
          "kind": "remove",
          "text": "    \"default_title\": \"Devolutions Password Manager\",",
          "before": 15
        },
        {
          "kind": "add",
          "text": "    \"default_title\": \"Devolutions Password Manager (PMA demo)\",",
          "after": 12
        },
        {
          "kind": "context",
          "text": "    \"default_icon\": {",
          "before": 16,
          "after": 13
        },
        {
          "kind": "context",
          "text": "      \"16\": \"assets/workspace/ws-16px-c.png\",",
          "before": 17,
          "after": 14
        },
        {
          "kind": "context",
          "text": "      \"32\": \"assets/workspace/ws-32px-c.png\"",
          "before": 18,
          "after": 15
        },
        {
          "kind": "hunk",
          "text": "@@ -80,6 +77,19 @@"
        },
        {
          "kind": "context",
          "text": "      \"all_frames\": true,",
          "before": 80,
          "after": 77
        },
        {
          "kind": "context",
          "text": "      \"match_about_blank\": true,",
          "before": 81,
          "after": 78
        },
        {
          "kind": "context",
          "text": "      \"world\": \"MAIN\"",
          "before": 82,
          "after": 79
        },
        {
          "kind": "add",
          "text": "    },",
          "after": 80
        },
        {
          "kind": "add",
          "text": "    {",
          "after": 81
        },
        {
          "kind": "add",
          "text": "      \"matches\": [",
          "after": 82
        },
        {
          "kind": "add",
          "text": "        \"http://localhost/*\",",
          "after": 83
        },
        {
          "kind": "add",
          "text": "        \"http://127.0.0.1/*\",",
          "after": 84
        },
        {
          "kind": "add",
          "text": "        \"https://pma-rs.pages.dev/*\",",
          "after": 85
        },
        {
          "kind": "add",
          "text": "        \"https://pma-cs.pages.dev/*\"",
          "after": 86
        },
        {
          "kind": "add",
          "text": "      ],",
          "after": 87
        },
        {
          "kind": "add",
          "text": "      \"js\": [",
          "after": 88
        },
        {
          "kind": "add",
          "text": "        \"pma-demo/content.js\"",
          "after": 89
        },
        {
          "kind": "add",
          "text": "      ],",
          "after": 90
        },
        {
          "kind": "add",
          "text": "      \"run_at\": \"document_start\",",
          "after": 91
        },
        {
          "kind": "add",
          "text": "      \"all_frames\": false",
          "after": 92
        },
        {
          "kind": "context",
          "text": "    }",
          "before": 83,
          "after": 93
        },
        {
          "kind": "context",
          "text": "  ],",
          "before": 84,
          "after": 94
        },
        {
          "kind": "context",
          "text": "  \"web_accessible_resources\": [{",
          "before": 85,
          "after": 95
        }
      ],
      "note": "Exact file diff, with three lines of unchanged context around each edit. Original formatting is preserved.",
      "addedLines": 16,
      "removedLines": 6,
      "before": {
        "url": "/downloads/extension-review/manifest.json.original.txt",
        "sha256": "993612d8c04e717dd82c6a6694ad6f64dad751b7f8c0274b169061aaa56cc733",
        "bytes": 3237
      },
      "after": {
        "url": "/downloads/extension-review/manifest.json.modified.txt",
        "sha256": "bd9e949024939a895b4d54180f84af4e1e37862a38f4cf3352ec176bb835ae0c",
        "bytes": 3075
      }
    },
    {
      "path": "pma-demo/background.js",
      "status": "added",
      "rows": [
        {
          "kind": "add",
          "text": "(() => {",
          "after": 1
        },
        {
          "kind": "add",
          "text": "  // <define:PMA_BUILD_CONFIG>",
          "after": 2
        },
        {
          "kind": "add",
          "text": "  var define_PMA_BUILD_CONFIG_default = { trustedOrigins: [\"http://localhost:4200\", \"http://127.0.0.1:4200\", \"https://pma-rs.pages.dev\", \"https://pma-cs.pages.dev\"], serverBases: [\"https://vault.bitwarden.com\", \"https://vault.bitwarden.eu\", \"https://api.bitwarden.com\", \"https://api.bitwarden.eu\", \"https://identity.bitwarden.com\", \"https://identity.bitwarden.eu\"], provider: \"Devolutions Password Manager\", version: \"2026.2.0.5\" };",
          "after": 3
        },
        {
          "kind": "add",
          "text": "",
          "after": 4
        },
        {
          "kind": "add",
          "text": "  // ../../../pma-pages-deploy/bridge-source/extension/src/policy.ts",
          "after": 5
        },
        {
          "kind": "add",
          "text": "  var BRIDGE_CONFIG = typeof define_PMA_BUILD_CONFIG_default === \"undefined\" ? {",
          "after": 6
        },
        {
          "kind": "add",
          "text": "    trustedOrigins: [",
          "after": 7
        },
        {
          "kind": "add",
          "text": "      \"http://localhost:4200\",",
          "after": 8
        },
        {
          "kind": "add",
          "text": "      \"http://127.0.0.1:4200\",",
          "after": 9
        },
        {
          "kind": "add",
          "text": "      \"https://pma-rs.pages.dev\"",
          "after": 10
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 11
        },
        {
          "kind": "add",
          "text": "    serverBases: [",
          "after": 12
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.com\",",
          "after": 13
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.eu\",",
          "after": 14
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.com\",",
          "after": 15
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.eu\",",
          "after": 16
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.com\",",
          "after": 17
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.eu\"",
          "after": 18
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 19
        },
        {
          "kind": "add",
          "text": "    provider: \"PMA standalone demo\",",
          "after": 20
        },
        {
          "kind": "add",
          "text": "    version: \"0.0.4\"",
          "after": 21
        },
        {
          "kind": "add",
          "text": "  } : define_PMA_BUILD_CONFIG_default;",
          "after": 22
        },
        {
          "kind": "add",
          "text": "  function isRecord(value) {",
          "after": 23
        },
        {
          "kind": "add",
          "text": "    return typeof value === \"object\" && value !== null && !Array.isArray(value);",
          "after": 24
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 25
        },
        {
          "kind": "add",
          "text": "  function isTrustedOrigin(origin) {",
          "after": 26
        },
        {
          "kind": "add",
          "text": "    return BRIDGE_CONFIG.trustedOrigins.includes(origin);",
          "after": 27
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 28
        },
        {
          "kind": "add",
          "text": "  function isTrustedSender(sender) {",
          "after": 29
        },
        {
          "kind": "add",
          "text": "    if (sender.id !== chrome.runtime.id || sender.frameId !== 0 || sender.tab?.id === void 0 || !sender.url)",
          "after": 30
        },
        {
          "kind": "add",
          "text": "      return false;",
          "after": 31
        },
        {
          "kind": "add",
          "text": "    try {",
          "after": 32
        },
        {
          "kind": "add",
          "text": "      return isTrustedOrigin(new URL(sender.url).origin);",
          "after": 33
        },
        {
          "kind": "add",
          "text": "    } catch {",
          "after": 34
        },
        {
          "kind": "add",
          "text": "      return false;",
          "after": 35
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 36
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 37
        },
        {
          "kind": "add",
          "text": "  function serverRoute(url) {",
          "after": 38
        },
        {
          "kind": "add",
          "text": "    if (url.protocol !== \"https:\" || url.username || url.password || url.hash)",
          "after": 39
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Target URL rejected\");",
          "after": 40
        },
        {
          "kind": "add",
          "text": "    for (const base of BRIDGE_CONFIG.serverBases) {",
          "after": 41
        },
        {
          "kind": "add",
          "text": "      const root = new URL(base);",
          "after": 42
        },
        {
          "kind": "add",
          "text": "      const prefix = root.pathname.replace(/\\/$/, \"\");",
          "after": 43
        },
        {
          "kind": "add",
          "text": "      if (url.origin === root.origin && url.pathname.startsWith(`${prefix}/`))",
          "after": 44
        },
        {
          "kind": "add",
          "text": "        return url.pathname.slice(prefix.length);",
          "after": 45
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 46
        },
        {
          "kind": "add",
          "text": "    throw new Error(\"Target server is not configured\");",
          "after": 47
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 48
        },
        {
          "kind": "add",
          "text": "  function validateFetch(message) {",
          "after": 49
        },
        {
          "kind": "add",
          "text": "    if (typeof message.url !== \"string\" || message.url.length > 8192)",
          "after": 50
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Invalid request URL\");",
          "after": 51
        },
        {
          "kind": "add",
          "text": "    const url = new URL(message.url);",
          "after": 52
        },
        {
          "kind": "add",
          "text": "    const route = serverRoute(url);",
          "after": 53
        },
        {
          "kind": "add",
          "text": "    const options = message.options === void 0 ? {} : message.options;",
          "after": 54
        },
        {
          "kind": "add",
          "text": "    if (!isRecord(options)) throw new Error(\"Invalid request options\");",
          "after": 55
        },
        {
          "kind": "add",
          "text": "    if (Object.keys(options).some((key) => ![\"method\", \"body\", \"headers\"].includes(key)))",
          "after": 56
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Request option rejected\");",
          "after": 57
        },
        {
          "kind": "add",
          "text": "    const method = options.method ?? \"GET\";",
          "after": 58
        },
        {
          "kind": "add",
          "text": "    const getRoutes = [",
          "after": 59
        },
        {
          "kind": "add",
          "text": "      \"/api/sync\",",
          "after": 60
        },
        {
          "kind": "add",
          "text": "      \"/api/accounts/profile\",",
          "after": 61
        },
        {
          "kind": "add",
          "text": "      \"/api/folders\",",
          "after": 62
        },
        {
          "kind": "add",
          "text": "      \"/api/collections\",",
          "after": 63
        },
        {
          "kind": "add",
          "text": "      \"/sync\",",
          "after": 64
        },
        {
          "kind": "add",
          "text": "      \"/accounts/profile\",",
          "after": 65
        },
        {
          "kind": "add",
          "text": "      \"/folders\",",
          "after": 66
        },
        {
          "kind": "add",
          "text": "      \"/collections\"",
          "after": 67
        },
        {
          "kind": "add",
          "text": "    ];",
          "after": 68
        },
        {
          "kind": "add",
          "text": "    const postRoutes = [",
          "after": 69
        },
        {
          "kind": "add",
          "text": "      \"/identity/accounts/prelogin\",",
          "after": 70
        },
        {
          "kind": "add",
          "text": "      \"/identity/connect/token\",",
          "after": 71
        },
        {
          "kind": "add",
          "text": "      \"/accounts/prelogin\",",
          "after": 72
        },
        {
          "kind": "add",
          "text": "      \"/connect/token\"",
          "after": 73
        },
        {
          "kind": "add",
          "text": "    ];",
          "after": 74
        },
        {
          "kind": "add",
          "text": "    if (!(method === \"GET\" && (getRoutes.includes(route) || /^\\/(?:api\\/)?ciphers\\/[\\w-]+\\/details$/.test(route))) && !(method === \"POST\" && postRoutes.includes(route)))",
          "after": 75
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Request endpoint or method rejected\");",
          "after": 76
        },
        {
          "kind": "add",
          "text": "    if (url.search && !(route.endsWith(\"/sync\") && url.search === \"?excludeDomains=true\"))",
          "after": 77
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Request query rejected\");",
          "after": 78
        },
        {
          "kind": "add",
          "text": "    if (method === \"GET\" && options.body !== void 0) throw new Error(\"GET request body rejected\");",
          "after": 79
        },
        {
          "kind": "add",
          "text": "    if (options.body !== void 0 && (typeof options.body !== \"string\" || options.body.length > 65536))",
          "after": 80
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Request body rejected\");",
          "after": 81
        },
        {
          "kind": "add",
          "text": "    const headers = {};",
          "after": 82
        },
        {
          "kind": "add",
          "text": "    if (options.headers !== void 0) {",
          "after": 83
        },
        {
          "kind": "add",
          "text": "      if (!isRecord(options.headers)) throw new Error(\"Request headers rejected\");",
          "after": 84
        },
        {
          "kind": "add",
          "text": "      for (const [name, value] of Object.entries(options.headers)) {",
          "after": 85
        },
        {
          "kind": "add",
          "text": "        const key = name.toLowerCase();",
          "after": 86
        },
        {
          "kind": "add",
          "text": "        if (![",
          "after": 87
        },
        {
          "kind": "add",
          "text": "          \"authorization\",",
          "after": 88
        },
        {
          "kind": "add",
          "text": "          \"content-type\",",
          "after": 89
        },
        {
          "kind": "add",
          "text": "          \"accept\",",
          "after": 90
        },
        {
          "kind": "add",
          "text": "          \"device-type\",",
          "after": 91
        },
        {
          "kind": "add",
          "text": "          \"bitwarden-client-name\",",
          "after": 92
        },
        {
          "kind": "add",
          "text": "          \"bitwarden-client-version\",",
          "after": 93
        },
        {
          "kind": "add",
          "text": "          \"auth-email\"",
          "after": 94
        },
        {
          "kind": "add",
          "text": "        ].includes(key) || typeof value !== \"string\" || value.length > 16384 || /[\\r\\n]/.test(value))",
          "after": 95
        },
        {
          "kind": "add",
          "text": "          throw new Error(\"Request header rejected\");",
          "after": 96
        },
        {
          "kind": "add",
          "text": "        if (key === \"authorization\" && (method !== \"GET\" || !/^Bearer [A-Za-z0-9._~+\\/-]+=*$/.test(value)))",
          "after": 97
        },
        {
          "kind": "add",
          "text": "          throw new Error(\"Authorization header rejected\");",
          "after": 98
        },
        {
          "kind": "add",
          "text": "        if (key === \"content-type\" && ![\"application/json\", \"application/x-www-form-urlencoded\"].includes(value))",
          "after": 99
        },
        {
          "kind": "add",
          "text": "          throw new Error(\"Content type rejected\");",
          "after": 100
        },
        {
          "kind": "add",
          "text": "        headers[key] = value;",
          "after": 101
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 102
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 103
        },
        {
          "kind": "add",
          "text": "    return {",
          "after": 104
        },
        {
          "kind": "add",
          "text": "      url: url.href,",
          "after": 105
        },
        {
          "kind": "add",
          "text": "      options: {",
          "after": 106
        },
        {
          "kind": "add",
          "text": "        method,",
          "after": 107
        },
        {
          "kind": "add",
          "text": "        headers,",
          "after": 108
        },
        {
          "kind": "add",
          "text": "        ...options.body === void 0 ? {} : { body: options.body },",
          "after": 109
        },
        {
          "kind": "add",
          "text": "        credentials: \"omit\",",
          "after": 110
        },
        {
          "kind": "add",
          "text": "        redirect: \"error\",",
          "after": 111
        },
        {
          "kind": "add",
          "text": "        cache: \"no-store\",",
          "after": 112
        },
        {
          "kind": "add",
          "text": "        referrerPolicy: \"no-referrer\"",
          "after": 113
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 114
        },
        {
          "kind": "add",
          "text": "    };",
          "after": 115
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 116
        },
        {
          "kind": "add",
          "text": "  function validateSso(message) {",
          "after": 117
        },
        {
          "kind": "add",
          "text": "    if (typeof message.flowId !== \"string\" || !/^[\\w-]{1,128}$/.test(message.flowId) || typeof message.url !== \"string\" || message.url.length > 8192 || typeof message.redirectPrefix !== \"string\" || message.redirectPrefix.length > 256)",
          "after": 118
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"Invalid SSO request\");",
          "after": 119
        },
        {
          "kind": "add",
          "text": "    const url = new URL(message.url);",
          "after": 120
        },
        {
          "kind": "add",
          "text": "    const hash = url.hash;",
          "after": 121
        },
        {
          "kind": "add",
          "text": "    url.hash = \"\";",
          "after": 122
        },
        {
          "kind": "add",
          "text": "    const route = serverRoute(url);",
          "after": 123
        },
        {
          "kind": "add",
          "text": "    let params;",
          "after": 124
        },
        {
          "kind": "add",
          "text": "    if (route === \"/\" && hash.startsWith(\"#/sso?\") && !url.search)",
          "after": 125
        },
        {
          "kind": "add",
          "text": "      params = new URLSearchParams(hash.slice(6));",
          "after": 126
        },
        {
          "kind": "add",
          "text": "    else if ([\"/identity/connect/authorize\", \"/connect/authorize\"].includes(route) && !hash)",
          "after": 127
        },
        {
          "kind": "add",
          "text": "      params = url.searchParams;",
          "after": 128
        },
        {
          "kind": "add",
          "text": "    else throw new Error(\"SSO entry point rejected\");",
          "after": 129
        },
        {
          "kind": "add",
          "text": "    const single = (...keys) => {",
          "after": 130
        },
        {
          "kind": "add",
          "text": "      const values = keys.flatMap((key) => params.getAll(key));",
          "after": 131
        },
        {
          "kind": "add",
          "text": "      if (values.length !== 1) throw new Error(\"SSO parameter rejected\");",
          "after": 132
        },
        {
          "kind": "add",
          "text": "      return values[0];",
          "after": 133
        },
        {
          "kind": "add",
          "text": "    };",
          "after": 134
        },
        {
          "kind": "add",
          "text": "    const redirect = new URL(message.redirectPrefix);",
          "after": 135
        },
        {
          "kind": "add",
          "text": "    if (redirect.protocol !== \"http:\" || ![\"localhost\", \"127.0.0.1\"].includes(redirect.hostname) || redirect.port !== \"8065\" || redirect.pathname !== \"/\" || redirect.search || redirect.hash || redirect.username || redirect.password)",
          "after": 136
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"SSO redirect rejected\");",
          "after": 137
        },
        {
          "kind": "add",
          "text": "    const targetRedirect = single(\"redirectUri\", \"redirect_uri\");",
          "after": 138
        },
        {
          "kind": "add",
          "text": "    if (!targetRedirect || new URL(targetRedirect).href !== redirect.href)",
          "after": 139
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"SSO redirect mismatch\");",
          "after": 140
        },
        {
          "kind": "add",
          "text": "    const state = single(\"state\");",
          "after": 141
        },
        {
          "kind": "add",
          "text": "    const challenge = single(\"codeChallenge\", \"code_challenge\");",
          "after": 142
        },
        {
          "kind": "add",
          "text": "    if (!state || !/^[\\w-]{16,256}$/.test(state) || !challenge || !/^[\\w-]{43}$/.test(challenge) || single(\"clientId\", \"client_id\") !== \"cli\")",
          "after": 143
        },
        {
          "kind": "add",
          "text": "      throw new Error(\"SSO state or PKCE rejected\");",
          "after": 144
        },
        {
          "kind": "add",
          "text": "    return { url: message.url, redirectPrefix: redirect.href, state };",
          "after": 145
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 146
        },
        {
          "kind": "add",
          "text": "  function isMatchingRedirect(rawUrl, redirectPrefix, state) {",
          "after": 147
        },
        {
          "kind": "add",
          "text": "    try {",
          "after": 148
        },
        {
          "kind": "add",
          "text": "      if (rawUrl.length > 8192) return false;",
          "after": 149
        },
        {
          "kind": "add",
          "text": "      const url = new URL(rawUrl);",
          "after": 150
        },
        {
          "kind": "add",
          "text": "      const expected = new URL(redirectPrefix);",
          "after": 151
        },
        {
          "kind": "add",
          "text": "      const returnedState = url.searchParams.get(\"state\");",
          "after": 152
        },
        {
          "kind": "add",
          "text": "      const stateParts = returnedState?.split(\"_identifier=\");",
          "after": 153
        },
        {
          "kind": "add",
          "text": "      const matchingState = returnedState === state || stateParts?.length === 2 && stateParts[0] === state && stateParts[1].length > 0;",
          "after": 154
        },
        {
          "kind": "add",
          "text": "      return url.origin === expected.origin && url.pathname === expected.pathname && !url.hash && !url.username && !url.password && url.searchParams.getAll(\"state\").length === 1 && matchingState && (url.searchParams.getAll(\"code\").length === 1 && !!url.searchParams.get(\"code\") && !url.searchParams.has(\"error\") || url.searchParams.getAll(\"error\").length === 1 && !!url.searchParams.get(\"error\") && !url.searchParams.has(\"code\"));",
          "after": 155
        },
        {
          "kind": "add",
          "text": "    } catch {",
          "after": 156
        },
        {
          "kind": "add",
          "text": "      return false;",
          "after": 157
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 158
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 159
        },
        {
          "kind": "add",
          "text": "",
          "after": 160
        },
        {
          "kind": "add",
          "text": "  // ../../../pma-pages-deploy/bridge-source/extension/src/background.ts",
          "after": 161
        },
        {
          "kind": "add",
          "text": "  var STORAGE_KEY = \"pmaBridgeSsoPending\";",
          "after": 162
        },
        {
          "kind": "add",
          "text": "  var SSO_LIFETIME = 3e5;",
          "after": 163
        },
        {
          "kind": "add",
          "text": "  var queue = Promise.resolve();",
          "after": 164
        },
        {
          "kind": "add",
          "text": "  function withPending(action) {",
          "after": 165
        },
        {
          "kind": "add",
          "text": "    const operation = queue.then(async () => {",
          "after": 166
        },
        {
          "kind": "add",
          "text": "      const stored = await chrome.storage.session.get(STORAGE_KEY);",
          "after": 167
        },
        {
          "kind": "add",
          "text": "      const pending = stored[STORAGE_KEY] ?? {};",
          "after": 168
        },
        {
          "kind": "add",
          "text": "      try {",
          "after": 169
        },
        {
          "kind": "add",
          "text": "        await action(pending);",
          "after": 170
        },
        {
          "kind": "add",
          "text": "      } finally {",
          "after": 171
        },
        {
          "kind": "add",
          "text": "        await chrome.storage.session.set({ [STORAGE_KEY]: pending });",
          "after": 172
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 173
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 174
        },
        {
          "kind": "add",
          "text": "    queue = operation.catch(() => {",
          "after": 175
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 176
        },
        {
          "kind": "add",
          "text": "    return operation;",
          "after": 177
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 178
        },
        {
          "kind": "add",
          "text": "  async function deliver(flowId, info, result) {",
          "after": 179
        },
        {
          "kind": "add",
          "text": "    await chrome.tabs.sendMessage(",
          "after": 180
        },
        {
          "kind": "add",
          "text": "      info.webTabId,",
          "after": 181
        },
        {
          "kind": "add",
          "text": "      { namespace: \"pma-bridge\", kind: \"ssoCode\", flowId, ...result },",
          "after": 182
        },
        {
          "kind": "add",
          "text": "      info.documentId ? { documentId: info.documentId } : { frameId: 0 }",
          "after": 183
        },
        {
          "kind": "add",
          "text": "    ).catch(() => {",
          "after": 184
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 185
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 186
        },
        {
          "kind": "add",
          "text": "  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {",
          "after": 187
        },
        {
          "kind": "add",
          "text": "    if (!isRecord(message) || message.namespace !== \"pma-bridge\") return;",
          "after": 188
        },
        {
          "kind": "add",
          "text": "    if (!isTrustedSender(sender)) {",
          "after": 189
        },
        {
          "kind": "add",
          "text": "      sendResponse({ ok: false, error: \"Bridge origin rejected\" });",
          "after": 190
        },
        {
          "kind": "add",
          "text": "      return;",
          "after": 191
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 192
        },
        {
          "kind": "add",
          "text": "    if (message.kind === \"fetch\") {",
          "after": 193
        },
        {
          "kind": "add",
          "text": "      void proxyFetch(message).then(sendResponse);",
          "after": 194
        },
        {
          "kind": "add",
          "text": "      return true;",
          "after": 195
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 196
        },
        {
          "kind": "add",
          "text": "    if (message.kind === \"sso\") {",
          "after": 197
        },
        {
          "kind": "add",
          "text": "      void startSso(message, sender).then(() => sendResponse({ ok: true })).catch(() => sendResponse({ error: \"SSO request rejected or tab unavailable\" }));",
          "after": 198
        },
        {
          "kind": "add",
          "text": "      return true;",
          "after": 199
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 200
        },
        {
          "kind": "add",
          "text": "    if (message.kind === \"ssoCancel\" && typeof message.flowId === \"string\") {",
          "after": 201
        },
        {
          "kind": "add",
          "text": "      void cancelSso(message.flowId, sender).then(() => sendResponse({ ok: true })).catch(() => sendResponse({ error: \"SSO cancellation failed\" }));",
          "after": 202
        },
        {
          "kind": "add",
          "text": "      return true;",
          "after": 203
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 204
        },
        {
          "kind": "add",
          "text": "  });",
          "after": 205
        },
        {
          "kind": "add",
          "text": "  async function proxyFetch(message) {",
          "after": 206
        },
        {
          "kind": "add",
          "text": "    try {",
          "after": 207
        },
        {
          "kind": "add",
          "text": "      const request = validateFetch(message);",
          "after": 208
        },
        {
          "kind": "add",
          "text": "      const response = await fetch(request.url, {",
          "after": 209
        },
        {
          "kind": "add",
          "text": "        ...request.options,",
          "after": 210
        },
        {
          "kind": "add",
          "text": "        signal: AbortSignal.timeout(3e4)",
          "after": 211
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 212
        },
        {
          "kind": "add",
          "text": "      const reader = response.body?.getReader();",
          "after": 213
        },
        {
          "kind": "add",
          "text": "      const decoder = new TextDecoder();",
          "after": 214
        },
        {
          "kind": "add",
          "text": "      let body = \"\";",
          "after": 215
        },
        {
          "kind": "add",
          "text": "      let size = 0;",
          "after": 216
        },
        {
          "kind": "add",
          "text": "      if (reader) {",
          "after": 217
        },
        {
          "kind": "add",
          "text": "        while (true) {",
          "after": 218
        },
        {
          "kind": "add",
          "text": "          const { done, value } = await reader.read();",
          "after": 219
        },
        {
          "kind": "add",
          "text": "          if (done) break;",
          "after": 220
        },
        {
          "kind": "add",
          "text": "          size += value.byteLength;",
          "after": 221
        },
        {
          "kind": "add",
          "text": "          if (size > 64 * 1024 * 1024) {",
          "after": 222
        },
        {
          "kind": "add",
          "text": "            await reader.cancel();",
          "after": 223
        },
        {
          "kind": "add",
          "text": "            throw new Error(\"Response too large\");",
          "after": 224
        },
        {
          "kind": "add",
          "text": "          }",
          "after": 225
        },
        {
          "kind": "add",
          "text": "          body += decoder.decode(value, { stream: true });",
          "after": 226
        },
        {
          "kind": "add",
          "text": "        }",
          "after": 227
        },
        {
          "kind": "add",
          "text": "        body += decoder.decode();",
          "after": 228
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 229
        },
        {
          "kind": "add",
          "text": "      return { ok: response.ok, status: response.status, body };",
          "after": 230
        },
        {
          "kind": "add",
          "text": "    } catch {",
          "after": 231
        },
        {
          "kind": "add",
          "text": "      return { ok: false, error: \"Extension request rejected or network unavailable\" };",
          "after": 232
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 233
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 234
        },
        {
          "kind": "add",
          "text": "  async function startSso(message, sender) {",
          "after": 235
        },
        {
          "kind": "add",
          "text": "    const request = validateSso(message);",
          "after": 236
        },
        {
          "kind": "add",
          "text": "    const flowId = message.flowId;",
          "after": 237
        },
        {
          "kind": "add",
          "text": "    const webTabId = sender.tab.id;",
          "after": 238
        },
        {
          "kind": "add",
          "text": "    let createdTabId;",
          "after": 239
        },
        {
          "kind": "add",
          "text": "    try {",
          "after": 240
        },
        {
          "kind": "add",
          "text": "      await withPending(async (pending) => {",
          "after": 241
        },
        {
          "kind": "add",
          "text": "        await expirePending(pending);",
          "after": 242
        },
        {
          "kind": "add",
          "text": "        if (pending[flowId] || Object.values(pending).some((info) => info.webTabId === webTabId))",
          "after": 243
        },
        {
          "kind": "add",
          "text": "          throw new Error(\"SSO already active\");",
          "after": 244
        },
        {
          "kind": "add",
          "text": "        const tab = await chrome.tabs.create({ url: \"about:blank\" });",
          "after": 245
        },
        {
          "kind": "add",
          "text": "        if (tab.id === void 0) throw new Error(\"SSO tab unavailable\");",
          "after": 246
        },
        {
          "kind": "add",
          "text": "        createdTabId = tab.id;",
          "after": 247
        },
        {
          "kind": "add",
          "text": "        pending[flowId] = {",
          "after": 248
        },
        {
          "kind": "add",
          "text": "          webTabId,",
          "after": 249
        },
        {
          "kind": "add",
          "text": "          documentId: sender.documentId,",
          "after": 250
        },
        {
          "kind": "add",
          "text": "          authTabId: tab.id,",
          "after": 251
        },
        {
          "kind": "add",
          "text": "          redirectPrefix: request.redirectPrefix,",
          "after": 252
        },
        {
          "kind": "add",
          "text": "          state: request.state,",
          "after": 253
        },
        {
          "kind": "add",
          "text": "          expiresAt: Date.now() + SSO_LIFETIME",
          "after": 254
        },
        {
          "kind": "add",
          "text": "        };",
          "after": 255
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 256
        },
        {
          "kind": "add",
          "text": "    } catch (error) {",
          "after": 257
        },
        {
          "kind": "add",
          "text": "      if (createdTabId !== void 0) await chrome.tabs.remove(createdTabId).catch(() => {",
          "after": 258
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 259
        },
        {
          "kind": "add",
          "text": "      throw error;",
          "after": 260
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 261
        },
        {
          "kind": "add",
          "text": "    await withPending(async (pending) => {",
          "after": 262
        },
        {
          "kind": "add",
          "text": "      const info = pending[flowId];",
          "after": 263
        },
        {
          "kind": "add",
          "text": "      if (!info) return;",
          "after": 264
        },
        {
          "kind": "add",
          "text": "      try {",
          "after": 265
        },
        {
          "kind": "add",
          "text": "        await chrome.tabs.update(info.authTabId, { url: request.url });",
          "after": 266
        },
        {
          "kind": "add",
          "text": "      } catch {",
          "after": 267
        },
        {
          "kind": "add",
          "text": "        delete pending[flowId];",
          "after": 268
        },
        {
          "kind": "add",
          "text": "        await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 269
        },
        {
          "kind": "add",
          "text": "        });",
          "after": 270
        },
        {
          "kind": "add",
          "text": "        throw new Error(\"SSO navigation failed\");",
          "after": 271
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 272
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 273
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 274
        },
        {
          "kind": "add",
          "text": "  async function expirePending(pending) {",
          "after": 275
        },
        {
          "kind": "add",
          "text": "    for (const [flowId, info] of Object.entries(pending)) {",
          "after": 276
        },
        {
          "kind": "add",
          "text": "      if (info.expiresAt > Date.now()) continue;",
          "after": 277
        },
        {
          "kind": "add",
          "text": "      delete pending[flowId];",
          "after": 278
        },
        {
          "kind": "add",
          "text": "      await deliver(flowId, info, { error: \"SSO login timed out\" });",
          "after": 279
        },
        {
          "kind": "add",
          "text": "      await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 280
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 281
        },
        {
          "kind": "add",
          "text": "    }",
          "after": 282
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 283
        },
        {
          "kind": "add",
          "text": "  async function cancelSso(flowId, sender) {",
          "after": 284
        },
        {
          "kind": "add",
          "text": "    await withPending(async (pending) => {",
          "after": 285
        },
        {
          "kind": "add",
          "text": "      const info = pending[flowId];",
          "after": 286
        },
        {
          "kind": "add",
          "text": "      if (!info || info.webTabId !== sender.tab.id || info.documentId && info.documentId !== sender.documentId)",
          "after": 287
        },
        {
          "kind": "add",
          "text": "        return;",
          "after": 288
        },
        {
          "kind": "add",
          "text": "      delete pending[flowId];",
          "after": 289
        },
        {
          "kind": "add",
          "text": "      await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 290
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 291
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 292
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 293
        },
        {
          "kind": "add",
          "text": "  chrome.webRequest.onBeforeRequest.addListener(",
          "after": 294
        },
        {
          "kind": "add",
          "text": "    (details) => {",
          "after": 295
        },
        {
          "kind": "add",
          "text": "      if (details.type !== \"main_frame\") return;",
          "after": 296
        },
        {
          "kind": "add",
          "text": "      void withPending(async (pending) => {",
          "after": 297
        },
        {
          "kind": "add",
          "text": "        await expirePending(pending);",
          "after": 298
        },
        {
          "kind": "add",
          "text": "        for (const [flowId, info] of Object.entries(pending)) {",
          "after": 299
        },
        {
          "kind": "add",
          "text": "          if (info.authTabId !== details.tabId || !isMatchingRedirect(details.url, info.redirectPrefix, info.state))",
          "after": 300
        },
        {
          "kind": "add",
          "text": "            continue;",
          "after": 301
        },
        {
          "kind": "add",
          "text": "          delete pending[flowId];",
          "after": 302
        },
        {
          "kind": "add",
          "text": "          await deliver(",
          "after": 303
        },
        {
          "kind": "add",
          "text": "            flowId,",
          "after": 304
        },
        {
          "kind": "add",
          "text": "            info,",
          "after": 305
        },
        {
          "kind": "add",
          "text": "            new URL(details.url).searchParams.has(\"error\") ? { error: \"SSO sign-in was declined. Try again.\" } : { url: details.url }",
          "after": 306
        },
        {
          "kind": "add",
          "text": "          );",
          "after": 307
        },
        {
          "kind": "add",
          "text": "          await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 308
        },
        {
          "kind": "add",
          "text": "          });",
          "after": 309
        },
        {
          "kind": "add",
          "text": "        }",
          "after": 310
        },
        {
          "kind": "add",
          "text": "      }).catch(() => {",
          "after": 311
        },
        {
          "kind": "add",
          "text": "      });",
          "after": 312
        },
        {
          "kind": "add",
          "text": "    },",
          "after": 313
        },
        {
          "kind": "add",
          "text": "    { urls: [\"http://localhost:8065/*\", \"http://127.0.0.1:8065/*\"], types: [\"main_frame\"] }",
          "after": 314
        },
        {
          "kind": "add",
          "text": "  );",
          "after": 315
        },
        {
          "kind": "add",
          "text": "  chrome.tabs.onRemoved.addListener((tabId) => {",
          "after": 316
        },
        {
          "kind": "add",
          "text": "    void withPending(async (pending) => {",
          "after": 317
        },
        {
          "kind": "add",
          "text": "      await expirePending(pending);",
          "after": 318
        },
        {
          "kind": "add",
          "text": "      for (const [flowId, info] of Object.entries(pending)) {",
          "after": 319
        },
        {
          "kind": "add",
          "text": "        if (info.webTabId !== tabId && info.authTabId !== tabId) continue;",
          "after": 320
        },
        {
          "kind": "add",
          "text": "        delete pending[flowId];",
          "after": 321
        },
        {
          "kind": "add",
          "text": "        if (info.authTabId === tabId) await deliver(flowId, info, { error: \"SSO tab closed\" });",
          "after": 322
        },
        {
          "kind": "add",
          "text": "        else await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 323
        },
        {
          "kind": "add",
          "text": "        });",
          "after": 324
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 325
        },
        {
          "kind": "add",
          "text": "    }).catch(() => {",
          "after": 326
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 327
        },
        {
          "kind": "add",
          "text": "  });",
          "after": 328
        },
        {
          "kind": "add",
          "text": "  chrome.tabs.onUpdated.addListener((tabId, change) => {",
          "after": 329
        },
        {
          "kind": "add",
          "text": "    if (change.status !== \"loading\") return;",
          "after": 330
        },
        {
          "kind": "add",
          "text": "    void withPending(async (pending) => {",
          "after": 331
        },
        {
          "kind": "add",
          "text": "      for (const [flowId, info] of Object.entries(pending)) {",
          "after": 332
        },
        {
          "kind": "add",
          "text": "        if (info.webTabId !== tabId) continue;",
          "after": 333
        },
        {
          "kind": "add",
          "text": "        delete pending[flowId];",
          "after": 334
        },
        {
          "kind": "add",
          "text": "        await chrome.tabs.remove(info.authTabId).catch(() => {",
          "after": 335
        },
        {
          "kind": "add",
          "text": "        });",
          "after": 336
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 337
        },
        {
          "kind": "add",
          "text": "    }).catch(() => {",
          "after": 338
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 339
        },
        {
          "kind": "add",
          "text": "  });",
          "after": 340
        },
        {
          "kind": "add",
          "text": "})();",
          "after": 341
        }
      ],
      "addedLines": 341,
      "removedLines": 0,
      "after": {
        "url": "/downloads/extension-review/pma-demo--background.js.modified.txt",
        "sha256": "06d832e925b1427885b0569d86a4ee63288b95d9fe9dc5d9a76a9cb65090d53f",
        "bytes": 14231
      }
    },
    {
      "path": "pma-demo/content.js",
      "status": "added",
      "rows": [
        {
          "kind": "add",
          "text": "(() => {",
          "after": 1
        },
        {
          "kind": "add",
          "text": "  // <define:PMA_BUILD_CONFIG>",
          "after": 2
        },
        {
          "kind": "add",
          "text": "  var define_PMA_BUILD_CONFIG_default = { trustedOrigins: [\"http://localhost:4200\", \"http://127.0.0.1:4200\", \"https://pma-rs.pages.dev\", \"https://pma-cs.pages.dev\"], serverBases: [\"https://vault.bitwarden.com\", \"https://vault.bitwarden.eu\", \"https://api.bitwarden.com\", \"https://api.bitwarden.eu\", \"https://identity.bitwarden.com\", \"https://identity.bitwarden.eu\"], provider: \"Devolutions Password Manager\", version: \"2026.2.0.5\" };",
          "after": 3
        },
        {
          "kind": "add",
          "text": "",
          "after": 4
        },
        {
          "kind": "add",
          "text": "  // ../../../pma-pages-deploy/bridge-source/extension/src/policy.ts",
          "after": 5
        },
        {
          "kind": "add",
          "text": "  var BRIDGE_CONFIG = typeof define_PMA_BUILD_CONFIG_default === \"undefined\" ? {",
          "after": 6
        },
        {
          "kind": "add",
          "text": "    trustedOrigins: [",
          "after": 7
        },
        {
          "kind": "add",
          "text": "      \"http://localhost:4200\",",
          "after": 8
        },
        {
          "kind": "add",
          "text": "      \"http://127.0.0.1:4200\",",
          "after": 9
        },
        {
          "kind": "add",
          "text": "      \"https://pma-rs.pages.dev\"",
          "after": 10
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 11
        },
        {
          "kind": "add",
          "text": "    serverBases: [",
          "after": 12
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.com\",",
          "after": 13
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.eu\",",
          "after": 14
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.com\",",
          "after": 15
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.eu\",",
          "after": 16
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.com\",",
          "after": 17
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.eu\"",
          "after": 18
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 19
        },
        {
          "kind": "add",
          "text": "    provider: \"PMA standalone demo\",",
          "after": 20
        },
        {
          "kind": "add",
          "text": "    version: \"0.0.4\"",
          "after": 21
        },
        {
          "kind": "add",
          "text": "  } : define_PMA_BUILD_CONFIG_default;",
          "after": 22
        },
        {
          "kind": "add",
          "text": "  function isRecord(value) {",
          "after": 23
        },
        {
          "kind": "add",
          "text": "    return typeof value === \"object\" && value !== null && !Array.isArray(value);",
          "after": 24
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 25
        },
        {
          "kind": "add",
          "text": "  function isTrustedOrigin(origin) {",
          "after": 26
        },
        {
          "kind": "add",
          "text": "    return BRIDGE_CONFIG.trustedOrigins.includes(origin);",
          "after": 27
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 28
        },
        {
          "kind": "add",
          "text": "",
          "after": 29
        },
        {
          "kind": "add",
          "text": "  // ../../../pma-pages-deploy/bridge-source/extension/src/content.ts",
          "after": 30
        },
        {
          "kind": "add",
          "text": "  if (window === window.top && isTrustedOrigin(location.origin)) {",
          "after": 31
        },
        {
          "kind": "add",
          "text": "    const reply = (message) => window.postMessage({ source: \"pma-ext\", ...message }, location.origin);",
          "after": 32
        },
        {
          "kind": "add",
          "text": "    window.addEventListener(\"message\", (event) => {",
          "after": 33
        },
        {
          "kind": "add",
          "text": "      const message = event.data;",
          "after": 34
        },
        {
          "kind": "add",
          "text": "      if (event.source !== window || event.origin !== location.origin || !isRecord(message)) return;",
          "after": 35
        },
        {
          "kind": "add",
          "text": "      if (message.source !== \"pma-page\") return;",
          "after": 36
        },
        {
          "kind": "add",
          "text": "      if (message.kind === \"ping\" && typeof message.id === \"string\") {",
          "after": 37
        },
        {
          "kind": "add",
          "text": "        reply({",
          "after": 38
        },
        {
          "kind": "add",
          "text": "          kind: \"pong\",",
          "after": 39
        },
        {
          "kind": "add",
          "text": "          id: message.id,",
          "after": 40
        },
        {
          "kind": "add",
          "text": "          provider: BRIDGE_CONFIG.provider,",
          "after": 41
        },
        {
          "kind": "add",
          "text": "          version: BRIDGE_CONFIG.version,",
          "after": 42
        },
        {
          "kind": "add",
          "text": "          sso: true",
          "after": 43
        },
        {
          "kind": "add",
          "text": "        });",
          "after": 44
        },
        {
          "kind": "add",
          "text": "        return;",
          "after": 45
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 46
        },
        {
          "kind": "add",
          "text": "      if (message.kind === \"fetch\" && typeof message.id === \"string\") {",
          "after": 47
        },
        {
          "kind": "add",
          "text": "        chrome.runtime.sendMessage(",
          "after": 48
        },
        {
          "kind": "add",
          "text": "          { namespace: \"pma-bridge\", kind: \"fetch\", url: message.url, options: message.options },",
          "after": 49
        },
        {
          "kind": "add",
          "text": "          (response) => {",
          "after": 50
        },
        {
          "kind": "add",
          "text": "            const error = chrome.runtime.lastError;",
          "after": 51
        },
        {
          "kind": "add",
          "text": "            reply({",
          "after": 52
        },
        {
          "kind": "add",
          "text": "              kind: \"fetch\",",
          "after": 53
        },
        {
          "kind": "add",
          "text": "              id: message.id,",
          "after": 54
        },
        {
          "kind": "add",
          "text": "              response: error || !response ? { ok: false, error: \"Extension bridge unavailable\" } : response",
          "after": 55
        },
        {
          "kind": "add",
          "text": "            });",
          "after": 56
        },
        {
          "kind": "add",
          "text": "          }",
          "after": 57
        },
        {
          "kind": "add",
          "text": "        );",
          "after": 58
        },
        {
          "kind": "add",
          "text": "        return;",
          "after": 59
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 60
        },
        {
          "kind": "add",
          "text": "      if (message.kind === \"sso\" && typeof message.flowId === \"string\") {",
          "after": 61
        },
        {
          "kind": "add",
          "text": "        chrome.runtime.sendMessage(",
          "after": 62
        },
        {
          "kind": "add",
          "text": "          {",
          "after": 63
        },
        {
          "kind": "add",
          "text": "            namespace: \"pma-bridge\",",
          "after": 64
        },
        {
          "kind": "add",
          "text": "            kind: \"sso\",",
          "after": 65
        },
        {
          "kind": "add",
          "text": "            flowId: message.flowId,",
          "after": 66
        },
        {
          "kind": "add",
          "text": "            url: message.url,",
          "after": 67
        },
        {
          "kind": "add",
          "text": "            redirectPrefix: message.redirectPrefix",
          "after": 68
        },
        {
          "kind": "add",
          "text": "          },",
          "after": 69
        },
        {
          "kind": "add",
          "text": "          (response) => {",
          "after": 70
        },
        {
          "kind": "add",
          "text": "            const error = chrome.runtime.lastError;",
          "after": 71
        },
        {
          "kind": "add",
          "text": "            if (error || response?.error)",
          "after": 72
        },
        {
          "kind": "add",
          "text": "              reply({",
          "after": 73
        },
        {
          "kind": "add",
          "text": "                kind: \"ssoCode\",",
          "after": 74
        },
        {
          "kind": "add",
          "text": "                flowId: message.flowId,",
          "after": 75
        },
        {
          "kind": "add",
          "text": "                error: response?.error ?? \"Extension bridge unavailable\"",
          "after": 76
        },
        {
          "kind": "add",
          "text": "              });",
          "after": 77
        },
        {
          "kind": "add",
          "text": "          }",
          "after": 78
        },
        {
          "kind": "add",
          "text": "        );",
          "after": 79
        },
        {
          "kind": "add",
          "text": "        return;",
          "after": 80
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 81
        },
        {
          "kind": "add",
          "text": "      if (message.kind === \"ssoCancel\" && typeof message.flowId === \"string\") {",
          "after": 82
        },
        {
          "kind": "add",
          "text": "        chrome.runtime.sendMessage(",
          "after": 83
        },
        {
          "kind": "add",
          "text": "          { namespace: \"pma-bridge\", kind: \"ssoCancel\", flowId: message.flowId },",
          "after": 84
        },
        {
          "kind": "add",
          "text": "          () => {",
          "after": 85
        },
        {
          "kind": "add",
          "text": "            void chrome.runtime.lastError;",
          "after": 86
        },
        {
          "kind": "add",
          "text": "          }",
          "after": 87
        },
        {
          "kind": "add",
          "text": "        );",
          "after": 88
        },
        {
          "kind": "add",
          "text": "      }",
          "after": 89
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 90
        },
        {
          "kind": "add",
          "text": "    chrome.runtime.onMessage.addListener((message, sender) => {",
          "after": 91
        },
        {
          "kind": "add",
          "text": "      if (sender.id !== chrome.runtime.id || !isRecord(message) || message.namespace !== \"pma-bridge\" || message.kind !== \"ssoCode\")",
          "after": 92
        },
        {
          "kind": "add",
          "text": "        return;",
          "after": 93
        },
        {
          "kind": "add",
          "text": "      reply({ kind: \"ssoCode\", flowId: message.flowId, url: message.url, error: message.error });",
          "after": 94
        },
        {
          "kind": "add",
          "text": "    });",
          "after": 95
        },
        {
          "kind": "add",
          "text": "  }",
          "after": 96
        },
        {
          "kind": "add",
          "text": "})();",
          "after": 97
        }
      ],
      "addedLines": 97,
      "removedLines": 0,
      "after": {
        "url": "/downloads/extension-review/pma-demo--content.js.modified.txt",
        "sha256": "e4dc9f15d21fca6325108926b35dcc2ea69612cbc0ec22166522cb66383597af",
        "bytes": 3889
      }
    },
    {
      "path": "pma-demo/provenance.json",
      "status": "added",
      "rows": [
        {
          "kind": "add",
          "text": "{",
          "after": 1
        },
        {
          "kind": "add",
          "text": "  \"sourceName\": \"Devolutions Password Manager\",",
          "after": 2
        },
        {
          "kind": "add",
          "text": "  \"sourceVersion\": \"2026.2.0.5\",",
          "after": 3
        },
        {
          "kind": "add",
          "text": "  \"sourceExtensionId\": \"neimonjjffhehnojilepgfejkneaidmo\",",
          "after": 4
        },
        {
          "kind": "add",
          "text": "  \"storeUrl\": \"https://chromewebstore.google.com/detail/neimonjjffhehnojilepgfejkneaidmo\",",
          "after": 5
        },
        {
          "kind": "add",
          "text": "  \"originalManifestSha256\": \"993612d8c04e717dd82c6a6694ad6f64dad751b7f8c0274b169061aaa56cc733\",",
          "after": 6
        },
        {
          "kind": "add",
          "text": "  \"originalWorkerSha256\": \"5f04638e3b022c0524d7c5252e44c9ec2bb3dfe87e30749fc99bbaabe7444c9d\",",
          "after": 7
        },
        {
          "kind": "add",
          "text": "  \"config\": {",
          "after": 8
        },
        {
          "kind": "add",
          "text": "    \"trustedOrigins\": [",
          "after": 9
        },
        {
          "kind": "add",
          "text": "      \"http://localhost:4200\",",
          "after": 10
        },
        {
          "kind": "add",
          "text": "      \"http://127.0.0.1:4200\",",
          "after": 11
        },
        {
          "kind": "add",
          "text": "      \"https://pma-rs.pages.dev\",",
          "after": 12
        },
        {
          "kind": "add",
          "text": "      \"https://pma-cs.pages.dev\"",
          "after": 13
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 14
        },
        {
          "kind": "add",
          "text": "    \"serverBases\": [",
          "after": 15
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.com\",",
          "after": 16
        },
        {
          "kind": "add",
          "text": "      \"https://vault.bitwarden.eu\",",
          "after": 17
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.com\",",
          "after": 18
        },
        {
          "kind": "add",
          "text": "      \"https://api.bitwarden.eu\",",
          "after": 19
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.com\",",
          "after": 20
        },
        {
          "kind": "add",
          "text": "      \"https://identity.bitwarden.eu\"",
          "after": 21
        },
        {
          "kind": "add",
          "text": "    ],",
          "after": 22
        },
        {
          "kind": "add",
          "text": "    \"provider\": \"Devolutions Password Manager\",",
          "after": 23
        },
        {
          "kind": "add",
          "text": "    \"version\": \"2026.2.0.5\"",
          "after": 24
        },
        {
          "kind": "add",
          "text": "  },",
          "after": 25
        },
        {
          "kind": "add",
          "text": "  \"permissionsAdded\": [],",
          "after": 26
        },
        {
          "kind": "add",
          "text": "  \"hostPermissionsAdded\": []",
          "after": 27
        },
        {
          "kind": "add",
          "text": "}",
          "after": 28
        }
      ],
      "addedLines": 28,
      "removedLines": 0,
      "after": {
        "url": "/downloads/extension-review/pma-demo--provenance.json.modified.txt",
        "sha256": "8a06ed4b734a5b564ec3f65baebfb242496bf2f78897fd6732f236918e670171",
        "bytes": 983
      }
    }
  ]
}
