From a0ea59c528fc93b832d75ed73ead3db1ad90d74b Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Wed, 30 Jul 2025 22:09:38 +0200 Subject: [PATCH] Initial Commit --- .ansible-lint | 4 + .editorconfig | 12 + .gitea/workflows/ansible-linters.yaml | 20 + .gitea/workflows/markdown-linters.yaml | 18 + .gitignore | 2 + .markdownlint.yaml | 137 ++ .yamllint.yaml | 19 + LICENSE | 20 + README.md | 69 + defaults/main.yaml | 86 + meta/main.yml | 25 + package-lock.json | 1732 +++++++++++++++++ package.json | 19 + renovate.json | 9 + tasks/client_certificate.yaml | 112 ++ tasks/client_certificate_import.yaml | 19 + tasks/client_certificate_protected.yaml | 59 + tasks/client_certificate_unprotected.yaml | 54 + tasks/intermediate_certificate_authority.yaml | 112 ++ ...rmediate_certificate_authority_import.yaml | 19 + ...diate_certificate_authority_protected.yaml | 44 + ...ate_certificate_authority_unprotected.yaml | 38 + tasks/main.yaml | 26 + tasks/root_certificate_authority.yaml | 84 + tasks/root_certificate_authority_import.yaml | 19 + .../root_certificate_authority_protected.yaml | 26 + ...oot_certificate_authority_unprotected.yaml | 24 + 27 files changed, 2808 insertions(+) create mode 100644 .ansible-lint create mode 100644 .editorconfig create mode 100644 .gitea/workflows/ansible-linters.yaml create mode 100644 .gitea/workflows/markdown-linters.yaml create mode 100644 .gitignore create mode 100644 .markdownlint.yaml create mode 100644 .yamllint.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 defaults/main.yaml create mode 100644 meta/main.yml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 renovate.json create mode 100644 tasks/client_certificate.yaml create mode 100644 tasks/client_certificate_import.yaml create mode 100644 tasks/client_certificate_protected.yaml create mode 100644 tasks/client_certificate_unprotected.yaml create mode 100644 tasks/intermediate_certificate_authority.yaml create mode 100644 tasks/intermediate_certificate_authority_import.yaml create mode 100644 tasks/intermediate_certificate_authority_protected.yaml create mode 100644 tasks/intermediate_certificate_authority_unprotected.yaml create mode 100644 tasks/main.yaml create mode 100644 tasks/root_certificate_authority.yaml create mode 100644 tasks/root_certificate_authority_import.yaml create mode 100644 tasks/root_certificate_authority_protected.yaml create mode 100644 tasks/root_certificate_authority_unprotected.yaml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..23a163f --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,4 @@ +--- + +exclude_paths: +- .gitea/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ad4c311 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false diff --git a/.gitea/workflows/ansible-linters.yaml b/.gitea/workflows/ansible-linters.yaml new file mode 100644 index 0000000..15ae818 --- /dev/null +++ b/.gitea/workflows/ansible-linters.yaml @@ -0,0 +1,20 @@ +name: Ansible Linter + +on: + pull_request: + types: [ "opened", "reopened", "synchronize" ] + push: + branches: [ '**' ] + tags-ignore: [ '**' ] + +jobs: + ansible-lint: + runs-on: + - ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ansible-lint + uses: ansible/ansible-lint@v25.6.1 + with: + args: "--config-file .ansible-lint" + setup_python: "true" diff --git a/.gitea/workflows/markdown-linters.yaml b/.gitea/workflows/markdown-linters.yaml new file mode 100644 index 0000000..10aa128 --- /dev/null +++ b/.gitea/workflows/markdown-linters.yaml @@ -0,0 +1,18 @@ +name: Lint Markdown files + +on: + pull_request: + types: [ "opened", "reopened", "synchronize" ] + push: + branches: [ '**' ] + tags-ignore: [ '**' ] + +jobs: + markdown-lint: + runs-on: + - ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + - uses: DavidAnson/markdownlint-cli2-action@v20.0.0 + with: + globs: '**/*.md' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c3c141 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ansible +node_modules \ No newline at end of file diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..0cca4db --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,137 @@ +# markdownlint YAML configuration +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "atx" + +# MD004/ul-style - Unordered list style +MD004: + style: "dash" + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 120 + # Number of characters for headings + heading_line_length: 120 + # Number of characters for code blocks + code_block_line_length: 120 + # Include code blocks + code_blocks: false + # Include tables + tables: false + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "---" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: + - gitea + # Include code blocks + code_blocks: false + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: "fenced" + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence syle + style: "backtick" diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..e0e4d53 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,19 @@ +--- + +# +# Documentation: +# https://yamllint.readthedocs.io/en/stable/ +# + +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + indentation: + spaces: 2 + indent-sequences: false + line-length: + max: 360 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..89dc507 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2025 Markus Pesch + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bb8f21 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# certificate-authority + +This Ansible role can be used to create a root and intermediate certificate authority and issue client certificates from +them. Additionally offers the ansible role the feature to import the certificates of the authority into the systems +trust store. + +## Examples + +The following minimal example creates a root and intermediate certificate authority and issues a client certificate from +the intermediate certificate authority. + +```yaml +certificate_authority_client_skip: false +certificate_authority_client_common_name: "{{ inventory_hostname }}" +certificate_authority_client_subject_alternative_names: +- "{{ inventory_hostname }}" +- san.example.local +``` + +## Parameters + +### Root Certificate Authority (CA) + +| Name | Description | Value | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | +| `certificate_authority_root_ca_skip` | Skip creation or import of a root certificate authority in general. | `false` | +| `certificate_authority_root_ca_create` | Create root certificate from scratch or import via `certificate_authority_root_ca_tls` prefixed variables. | `true` | +| `certificate_authority_root_ca_import` | Import the TLS certificate of the root certificate authority into the systems trust store. | `true` | +| `certificate_authority_root_ca_path` | Directory where the private and public TLS key of the root certificate authority should be stored. | `/etc/ansible-playbook/pki/ca` | +| `certificate_authority_root_ca_common_name` | Common Name (CN) of the root certificate authority. | `Ansible Root CA` | +| `certificate_authority_root_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the root certificate authority. | `[]` | +| `certificate_authority_root_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+3650d` | +| `certificate_authority_root_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | +| `certificate_authority_root_ca_tls_key_content` | Content of a custom used root certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. | `""` | +| `certificate_authority_root_ca_tls_crt_content` | Content of a custom used certificate of the certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. | `""` | +| `certificate_authority_root_ca_tls_key_passphrase` | Passphrase for the private key of the generated or imported root certificate authority. | `""` | +| `certificate_authority_root_ca_tls_key_type` | Algorithm of the private key of the root certificate authority. | `RSA` | + +### Intermediate Certificate Authority (CA) + +| Name | Description | Value | +| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| `certificate_authority_intermediate_ca_skip` | Skip creation or import of a intermediate certificate authority in general. | `false` | +| `certificate_authority_intermediate_ca_create` | Create intermediate certificate from scratch or import via `certificate_authority_intermediate_ca_tls` prefixed variables. | `true` | +| `certificate_authority_intermediate_ca_path` | Directory where the private and public TLS key of the intermediate certificate authority should be stored. | `/etc/ansible-playbook/pki/intermediate` | +| `certificate_authority_intermediate_ca_common_name` | Common Name (CN) of the intermediate certificate authority. | `Ansible Intermediate CA` | +| `certificate_authority_intermediate_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the intermediate certificate authority. | `[]` | +| `certificate_authority_intermediate_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+1825d` | +| `certificate_authority_intermediate_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | +| `certificate_authority_intermediate_ca_tls_key_content` | Content of a custom used intermediate certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. | `""` | +| `certificate_authority_intermediate_ca_tls_crt_content` | Content of a custom used certificate of the certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. | `""` | +| `certificate_authority_intermediate_ca_tls_key_passphrase` | Passphrase for the private key of the generated or imported intermediate certificate authority. | `""` | +| `certificate_authority_intermediate_ca_tls_key_type` | Algorithm of the private key of the intermediate certificate authority. | `RSA` | + +### Client Certificate + +| Name | Description | Value | +| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------- | +| `certificate_authority_client_skip` | Skip creation or import of a client certificate in general. | `true` | +| `certificate_authority_client_create` | Create client certificate from scratch or import via `certificate_authority_client_tls` prefixed variables. | `true` | +| `certificate_authority_client_path` | Directory where the private and public TLS key of the client certificate authority should be stored. | `/etc/ansible-playbook/pki/client` | +| `certificate_authority_client_common_name` | Common Name (CN) of the client certificate. | `Ansible Client Certificate` | +| `certificate_authority_client_subject_alternative_names` | Subject Alternative Names (SAN) of the client certificate. | `[]` | +| `certificate_authority_client_not_after` | Time in the future from now when the TLS certificate should expire | `+397d` | +| `certificate_authority_client_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | +| `certificate_authority_client_tls_key_passphrase` | Passphrase for the private key of the generated or imported client certificate. | `""` | +| `certificate_authority_client_tls_key_type` | Algorithm of the private key of the client certificate. | `RSA` | +| `certificate_authority_client_tls_crt_content` | Passphrase for the private key of the generated or imported client certificate. | `""` | +| `certificate_authority_client_tls_key_content` | Algorithm of the private key of the client certificate | `""` | diff --git a/defaults/main.yaml b/defaults/main.yaml new file mode 100644 index 0000000..baed009 --- /dev/null +++ b/defaults/main.yaml @@ -0,0 +1,86 @@ +--- + +## @section Root Certificate Authority (CA) +## @param certificate_authority_root_ca_skip Skip creation or import of a root certificate authority in general. +## @param certificate_authority_root_ca_create Create root certificate from scratch or import via `certificate_authority_root_ca_tls` prefixed variables. +## @param certificate_authority_root_ca_import Import the TLS certificate of the root certificate authority into the systems trust store. +certificate_authority_root_ca_skip: false +certificate_authority_root_ca_create: true +certificate_authority_root_ca_import: true + +## @param certificate_authority_root_ca_path Directory where the private and public TLS key of the root certificate authority should be stored. +## @param certificate_authority_root_ca_common_name Common Name (CN) of the root certificate authority. +## @param certificate_authority_root_ca_subject_alternative_names Subject Alternative Names (SAN) of the root certificate authority. +## @param certificate_authority_root_ca_not_after Time in the future from now when the TLS certificate should expire +## @param certificate_authority_root_ca_not_before Time in the past from now when the TLS certificate should be valid. +certificate_authority_root_ca_path: "/etc/ansible-playbook/pki/ca" +certificate_authority_root_ca_common_name: "Ansible Root CA" +certificate_authority_root_ca_subject_alternative_names: [] +certificate_authority_root_ca_not_after: "+3650d" +certificate_authority_root_ca_not_before: "+0s" + +## @param certificate_authority_root_ca_tls_key_content Content of a custom used root certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. +## @param certificate_authority_root_ca_tls_crt_content Content of a custom used certificate of the certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. +certificate_authority_root_ca_tls_key_content: "" +certificate_authority_root_ca_tls_crt_content: "" + +## @param certificate_authority_root_ca_tls_key_passphrase Passphrase for the private key of the generated or imported root certificate authority. +## @param certificate_authority_root_ca_tls_key_type Algorithm of the private key of the root certificate authority. +certificate_authority_root_ca_tls_key_passphrase: "" +certificate_authority_root_ca_tls_key_type: "RSA" + + +## @section Intermediate Certificate Authority (CA) +## @param certificate_authority_intermediate_ca_skip Skip creation or import of a intermediate certificate authority in general. +## @param certificate_authority_intermediate_ca_create Create intermediate certificate from scratch or import via `certificate_authority_intermediate_ca_tls` prefixed variables. +certificate_authority_intermediate_ca_skip: false +certificate_authority_intermediate_ca_create: true + +## @param certificate_authority_intermediate_ca_path Directory where the private and public TLS key of the intermediate certificate authority should be stored. +## @param certificate_authority_intermediate_ca_common_name Common Name (CN) of the intermediate certificate authority. +## @param certificate_authority_intermediate_ca_subject_alternative_names Subject Alternative Names (SAN) of the intermediate certificate authority. +## @param certificate_authority_intermediate_ca_not_after Time in the future from now when the TLS certificate should expire +## @param certificate_authority_intermediate_ca_not_before Time in the past from now when the TLS certificate should be valid. +certificate_authority_intermediate_ca_path: "/etc/ansible-playbook/pki/intermediate" +certificate_authority_intermediate_ca_common_name: "Ansible Intermediate CA" +certificate_authority_intermediate_ca_subject_alternative_names: [] +certificate_authority_intermediate_ca_not_after: "+1825d" +certificate_authority_intermediate_ca_not_before: "+0s" + +## @param certificate_authority_intermediate_ca_tls_key_content Content of a custom used intermediate certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. +## @param certificate_authority_intermediate_ca_tls_crt_content Content of a custom used certificate of the certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. +certificate_authority_intermediate_ca_tls_key_content: "" +certificate_authority_intermediate_ca_tls_crt_content: "" + +## @param certificate_authority_intermediate_ca_tls_key_passphrase Passphrase for the private key of the generated or imported intermediate certificate authority. +## @param certificate_authority_intermediate_ca_tls_key_type Algorithm of the private key of the intermediate certificate authority. +certificate_authority_intermediate_ca_tls_key_passphrase: "" +certificate_authority_intermediate_ca_tls_key_type: "RSA" + + +## @section Client Certificate +## @param certificate_authority_client_skip Skip creation or import of a client certificate in general. +## @param certificate_authority_client_create Create client certificate from scratch or import via `certificate_authority_client_tls` prefixed variables. +certificate_authority_client_skip: true +certificate_authority_client_create: true + +## @param certificate_authority_client_path Directory where the private and public TLS key of the client certificate authority should be stored. +## @param certificate_authority_client_common_name Common Name (CN) of the client certificate. +## @param certificate_authority_client_subject_alternative_names Subject Alternative Names (SAN) of the client certificate. +## @param certificate_authority_client_not_after Time in the future from now when the TLS certificate should expire +## @param certificate_authority_client_not_before Time in the past from now when the TLS certificate should be valid. +certificate_authority_client_path: "/etc/ansible-playbook/pki/client" +certificate_authority_client_common_name: "Ansible Client Certificate" +certificate_authority_client_subject_alternative_names: [] +certificate_authority_client_not_after: "+397d" +certificate_authority_client_not_before: "+0s" + +## @param certificate_authority_client_tls_key_passphrase Passphrase for the private key of the generated or imported client certificate. +## @param certificate_authority_client_tls_key_type Algorithm of the private key of the client certificate. +certificate_authority_client_tls_key_passphrase: "" +certificate_authority_client_tls_key_type: "RSA" + +## @param certificate_authority_client_tls_crt_content Passphrase for the private key of the generated or imported client certificate. +## @param certificate_authority_client_tls_key_content Algorithm of the private key of the client certificate +certificate_authority_client_tls_crt_content: "" +certificate_authority_client_tls_key_content: "" diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..681157f --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,25 @@ +galaxy_info: + namespace: volker-raschek + role_name: "certificate_authority" + author: "Markus Pesch" + description: "Role to create and managed an existing PKI infrastructure" + company: "Cryptic Systems" + license: "MIT" + min_ansible_version: "2.9" + platforms: + - name: ArchLinux + versions: + - all + - name: Ubuntu + versions: + - all + - name: Fedora + versions: + - "35" + galaxy_tags: + - certificate-authority + - ca + - ssl + - tls + +dependencies: [] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a05709a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1732 @@ +{ + "name": "certificate-authority-ansible-role", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "certificate-authority-ansible-role", + "license": "MIT", + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdownlint-cli": "^0.45.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/@bitnami/readme-generator-for-helm": { + "version": "2.7.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.7.2.tgz", + "integrity": "sha512-7eXyJzxQTQj2ajpHlIhadciCCYWOqN8ieaweU25bStHOZowQ2c2CQyjO/bX4gxIf73LoRKxHhEYgLTllJY9SIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "commander": "^13.1.0", + "dot-object": "^2.1.5", + "lodash": "^4.17.21", + "markdown-table": "^2.0.0", + "yaml": "^2.7.0" + }, + "bin": { + "readme-generator": "bin/index.js" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dot-object": { + "version": "2.1.5", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/dot-object/-/dot-object-2.1.5.tgz", + "integrity": "sha512-xHF8EP4XH/Ba9fvAF2LDd5O3IITVolerVV6xvkxoM8zlGEiCUrggpAnHyOoKJKCrhvPcGATFAUwIujj7bRG5UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^6.1.0", + "glob": "^7.1.6" + }, + "bin": { + "dot-object": "bin/dot-object" + } + }, + "node_modules/dot-object/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.16.22", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdownlint": { + "version": "0.38.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.45.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz", + "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~13.1.0", + "glob": "~11.0.2", + "ignore": "~7.0.4", + "js-yaml": "~4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.1.0", + "markdownlint": "~0.38.0", + "minimatch": "~10.0.1", + "run-con": "~1.3.2", + "smol-toml": "~1.3.4" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smol-toml": { + "version": "1.3.4", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/smol-toml/-/smol-toml-1.3.4.tgz", + "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://repo-nexus.orbis.dedalus.com/nexus/repository/npm-all/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7d7ce5a --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "certificate-authority-ansible-role", + "homepage": "https://git.cryptic.systems/volker.raschel/certificate-authority-ansible-role.git", + "license": "MIT", + "private": true, + "engineStrict": true, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "scripts": { + "readme:lint": "markdownlint *.md -f", + "readme:parameters": "readme-generator -v defaults/main.yaml -r README.md" + }, + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdownlint-cli": "^0.45.0" + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..84f88f7 --- /dev/null +++ b/renovate.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>volker.raschek/renovate-config:default#master", + "local>volker.raschek/renovate-config:container#master", + "local>volker.raschek/renovate-config:actions#master", + "local>volker.raschek/renovate-config:regexp#master" + ] +} \ No newline at end of file diff --git a/tasks/client_certificate.yaml b/tasks/client_certificate.yaml new file mode 100644 index 0000000..b72b3b7 --- /dev/null +++ b/tasks/client_certificate.yaml @@ -0,0 +1,112 @@ +--- + +- name: Create directory to store tls keys and certificates of the client + ansible.builtin.file: + path: "{{ certificate_authority_client_path }}" + owner: "root" + group: "root" + mode: "0700" + state: directory + +- name: Create unprotected client certificate + ansible.builtin.include_tasks: client_certificate_unprotected.yaml + when: certificate_authority_client_create is defined and + certificate_authority_client_create and + certificate_authority_client_tls_key_passphrase is defined and + certificate_authority_client_tls_key_passphrase | length <= 0 + +- name: Create passphrase protected client certificate + ansible.builtin.include_tasks: client_certificate_unprotected.yaml + when: certificate_authority_client_create is defined and + certificate_authority_client_create and + certificate_authority_client_tls_key_passphrase is defined and + certificate_authority_client_tls_key_passphrase | length > 0 + +- name: Import client certificate + ansible.builtin.include_tasks: client_certificate_import.yaml + when: certificate_authority_client_create is defined and + not certificate_authority_client_create + +- name: Create certificate chain file + block: + - name: Check if intermediate certificate exists + ansible.builtin.stat: + path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + register: _stat_result + - name: Concatenate client certificate and intermediate certificate + vars: + _chain_files: + - "{{ certificate_authority_client_path }}/cert.pem" + - "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated chain file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_client_path }}/chain.pem" + owner: "root" + group: "root" + mode: "0644" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + +- name: Create certificate fullchain file + block: + - name: Check if intermediate chain exists + ansible.builtin.stat: + path: "{{ certificate_authority_intermediate_ca_path }}/chain.pem" + register: _stat_result + - name: Concatenate client certificate and intermediate chain file + vars: + _chain_files: + - "{{ certificate_authority_client_path }}/cert.pem" + - "{{ certificate_authority_intermediate_ca_path }}/chain.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated fullchain file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_client_path }}/fullchain.pem" + owner: "root" + group: "root" + mode: "0644" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + +- name: Create file with private key and fullchain file of the client + block: + - name: Check if fullchain exists + ansible.builtin.stat: + path: "{{ certificate_authority_client_path }}/fullchain.pem" + register: _stat_result + - name: Concatenate private key and fullchain file of the client + vars: + _chain_files: + - "{{ certificate_authority_client_path }}/privkey.pem" + - "{{ certificate_authority_client_path }}/fullchain.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_client_path }}/all.pem" + owner: "root" + group: "root" + mode: "0600" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists diff --git a/tasks/client_certificate_import.yaml b/tasks/client_certificate_import.yaml new file mode 100644 index 0000000..156f011 --- /dev/null +++ b/tasks/client_certificate_import.yaml @@ -0,0 +1,19 @@ +--- + +- name: Import private key of a client + ansible.builtin.copy: + content: "{{ certificate_authority_client_tls_key_content }}" + dest: "{{ certificate_authority_client_ca_path }}/privkey.pem" + owner: "root" + group: "root" + mode: "0600" + when: certificate_authority_client_tls_key_content | length > 0 + +- name: Import certificate of a client + ansible.builtin.copy: + content: "{{ certificate_authority_client_tls_crt_content }}" + dest: "{{ certificate_authority_client_tls_crt_content }}/cert.pem" + owner: "root" + group: "root" + mode: "0644" + when: certificate_authority_client_tls_crt_content | length > 0 diff --git a/tasks/client_certificate_protected.yaml b/tasks/client_certificate_protected.yaml new file mode 100644 index 0000000..9cae3ab --- /dev/null +++ b/tasks/client_certificate_protected.yaml @@ -0,0 +1,59 @@ +--- + +- name: Create private key for client + community.crypto.openssl_privatekey: + path: "{{ certificate_authority_client_path }}/privkey.pem" + type: "{{ certificate_authority_client_tls_key_type }}" + passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" + +- name: Create a certificate signing request (CSR) for client certificate without subject alternative names (SANs) + community.crypto.openssl_csr: + common_name: "{{ certificate_authority_client_common_name }}" + extendedKeyUsage: + - clientAuth + - serverAuth + path: "{{ certificate_authority_client_path }}/cert-req.pem" + privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" + privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" + when: | + certificate_authority_client_subject_alternative_names is not defined or + (certificate_authority_client_subject_alternative_names is defined and + certificate_authority_client_subject_alternative_names | length <= 0) + +- name: Create a certificate signing request (CSR) for client certificate with subject alternative names (SANs) + community.crypto.openssl_csr: + common_name: "{{ certificate_authority_client_common_name }}" + extendedKeyUsage: + - clientAuth + - serverAuth + path: "{{ certificate_authority_client_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" + privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" + subject_alt_name: "{{ certificate_authority_client_subject_alternative_names | map('regex_replace', '^', 'DNS:') | list | join(',') | quote }}" + when: certificate_authority_client_subject_alternative_names is defined and + certificate_authority_client_subject_alternative_names | length > 0 + +- name: Create signed client certificate - unprotected intermediate Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_client_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_client_not_after }}" + ownca_not_before: "{{ certificate_authority_client_not_before }}" + ownca_path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + ownca_privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + path: "{{ certificate_authority_client_path }}/cert.pem" + privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" + provider: ownca + when: certificate_authority_intermediate_ca_tls_key_passphrase | length <= 0 + +- name: Create signed client certificate - passphrase protected intermediate Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_client_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_client_not_after }}" + ownca_not_before: "{{ certificate_authority_client_not_before }}" + ownca_path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + ownca_privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + ownca_privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + path: "{{ certificate_authority_client_path }}/cert.pem" + privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" + provider: ownca + when: certificate_authority_intermediate_ca_tls_key_passphrase | length > 0 diff --git a/tasks/client_certificate_unprotected.yaml b/tasks/client_certificate_unprotected.yaml new file mode 100644 index 0000000..4b95fbb --- /dev/null +++ b/tasks/client_certificate_unprotected.yaml @@ -0,0 +1,54 @@ +--- + +- name: Create private key for client + community.crypto.openssl_privatekey: + path: "{{ certificate_authority_client_path }}/privkey.pem" + type: "{{ certificate_authority_client_tls_key_type }}" + +- name: Create a certificate signing request (CSR) for client certificate without subject alternative names (SANs) + community.crypto.openssl_csr: + common_name: "{{ certificate_authority_client_common_name }}" + extendedKeyUsage: + - clientAuth + - serverAuth + path: "{{ certificate_authority_client_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" + when: | + certificate_authority_client_subject_alternative_names is not defined or + (certificate_authority_client_subject_alternative_names is defined and + certificate_authority_client_subject_alternative_names | length <= 0) + +- name: Create a certificate signing request (CSR) for client certificate with subject alternative names (SANs) + community.crypto.openssl_csr: + common_name: "{{ certificate_authority_client_common_name }}" + extendedKeyUsage: + - clientAuth + - serverAuth + path: "{{ certificate_authority_client_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" + subject_alt_name: "{{ certificate_authority_client_subject_alternative_names | map('regex_replace', '^', 'DNS:') | list | join(',') | quote }}" + when: certificate_authority_client_subject_alternative_names is defined and + certificate_authority_client_subject_alternative_names | length > 0 + +- name: Create signed client certificate - unprotected intermediate Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_client_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_client_not_after }}" + ownca_not_before: "{{ certificate_authority_client_not_before }}" + ownca_path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + ownca_privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + path: "{{ certificate_authority_client_path }}/cert.pem" + provider: ownca + when: certificate_authority_intermediate_ca_tls_key_passphrase | length <= 0 + +- name: Create signed client certificate - passphrase protected intermediate Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_client_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_client_not_after }}" + ownca_not_before: "{{ certificate_authority_client_not_before }}" + ownca_path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + ownca_privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + ownca_privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + path: "{{ certificate_authority_client_path }}/cert.pem" + provider: ownca + when: certificate_authority_intermediate_ca_tls_key_passphrase | length > 0 diff --git a/tasks/intermediate_certificate_authority.yaml b/tasks/intermediate_certificate_authority.yaml new file mode 100644 index 0000000..c7d71d4 --- /dev/null +++ b/tasks/intermediate_certificate_authority.yaml @@ -0,0 +1,112 @@ +--- + +- name: Create directory to store tls keys and certificates of the intermediate CA + ansible.builtin.file: + path: "{{ certificate_authority_intermediate_ca_path }}" + owner: "root" + group: "root" + mode: "0700" + state: "directory" + +- name: Create unprotected intermediate Certificate Authority (CA) + ansible.builtin.include_tasks: intermediate_certificate_authority_unprotected.yaml + when: certificate_authority_intermediate_ca_create is defined and + certificate_authority_intermediate_ca_create and + certificate_authority_intermediate_ca_tls_key_passphrase is defined and + certificate_authority_intermediate_ca_tls_key_passphrase | length <= 0 + +- name: Create passphrase protected intermediate Certificate Authority (CA) + ansible.builtin.include_tasks: intermediate_certificate_authority_unprotected.yaml + when: certificate_authority_intermediate_ca_create is defined and + certificate_authority_intermediate_ca_create and + certificate_authority_intermediate_ca_tls_key_passphrase is defined and + certificate_authority_intermediate_ca_tls_key_passphrase | length > 0 + +- name: Import intermediate Certificate Authority (CA) + ansible.builtin.include_tasks: intermediate_certificate_authority_import.yaml + when: certificate_authority_intermediate_ca_create is defined and + not certificate_authority_intermediate_ca_create + +- name: Create certificate chain file + block: + - name: Check if root certificate exists + ansible.builtin.stat: + path: "{{ certificate_authority_root_ca_path }}/cert.pem" + register: _stat_result + - name: Concatenate intermediate certificate and root certificate + vars: + _chain_files: + - "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + - "{{ certificate_authority_root_ca_path }}/cert.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated chain file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_intermediate_ca_path }}/chain.pem" + owner: "root" + group: "root" + mode: "0644" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + +- name: Create certificate fullchain file + block: + - name: Check if root chain exists + ansible.builtin.stat: + path: "{{ certificate_authority_root_ca_path }}/chain.pem" + register: _stat_result + - name: Concatenate intermediate certificate and root chain file + vars: + _chain_files: + - "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + - "{{ certificate_authority_root_ca_path }}/chain.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated fullchain file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_intermediate_ca_path }}/fullchain.pem" + owner: "root" + group: "root" + mode: "0644" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + +- name: Create file with private key and fullchain file of intermediate Certificate Authority (CA) + block: + - name: Check if private key exists + ansible.builtin.stat: + path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + register: _stat_result + - name: Concatenate private key and fullchain file of intermediate Certificate Authority (CA) + vars: + _chain_files: + - "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + - "{{ certificate_authority_intermediate_ca_path }}/fullchain.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_intermediate_ca_path }}/all.pem" + owner: "root" + group: "root" + mode: "0600" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists diff --git a/tasks/intermediate_certificate_authority_import.yaml b/tasks/intermediate_certificate_authority_import.yaml new file mode 100644 index 0000000..c4f302a --- /dev/null +++ b/tasks/intermediate_certificate_authority_import.yaml @@ -0,0 +1,19 @@ +--- + +- name: Import private key of intermediate Certificate Authority (CA) + ansible.builtin.copy: + content: "{{ certificate_authority_intermediate_ca_tls_key_content }}" + dest: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + owner: root + group: root + mode: "0600" + when: certificate_authority_intermediate_ca_tls_key_content | length > 0 + +- name: Import certificate of intermediate Certificate Authority (CA) + ansible.builtin.copy: + content: "{{ certificate_authority_intermediate_ca_tls_crt_content }}" + dest: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + owner: root + group: root + mode: "0644" + when: certificate_authority_intermediate_ca_tls_crt_content | length > 0 diff --git a/tasks/intermediate_certificate_authority_protected.yaml b/tasks/intermediate_certificate_authority_protected.yaml new file mode 100644 index 0000000..8106c50 --- /dev/null +++ b/tasks/intermediate_certificate_authority_protected.yaml @@ -0,0 +1,44 @@ +--- + +- name: Create private key for intermediate CA + community.crypto.openssl_privatekey: + passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + type: "{{ certificate_authority_intermediate_ca_tls_key_type }}" + +- name: Create a certificate signing request (CSR) for intermediate CA + community.crypto.openssl_csr: + basic_constraints: + - "CA:TRUE" + common_name: "{{ certificate_authority_intermediate_ca_common_name }}" + path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + use_common_name_for_san: false + +- name: Create signed client certificate - unprotected root Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_intermediate_ca_not_after }}" + ownca_not_before: "{{ certificate_authority_intermediate_ca_not_before }}" + ownca_path: "{{ certificate_authority_root_ca_path }}/cert.pem" + ownca_privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + provider: ownca + when: certificate_authority_root_ca_tls_key_passphrase is defined and + certificate_authority_root_ca_tls_key_passphrase | length <= 0 + +- name: Create signed client certificate - passphrase protected root Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_intermediate_ca_not_after }}" + ownca_not_before: "{{ certificate_authority_intermediate_ca_not_before }}" + ownca_path: "{{ certificate_authority_root_ca_path }}/cert.pem" + ownca_privatekey_passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}" + ownca_privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" + provider: ownca + when: certificate_authority_root_ca_tls_key_passphrase is defined and + certificate_authority_root_ca_tls_key_passphrase | length > 0 diff --git a/tasks/intermediate_certificate_authority_unprotected.yaml b/tasks/intermediate_certificate_authority_unprotected.yaml new file mode 100644 index 0000000..65fce1b --- /dev/null +++ b/tasks/intermediate_certificate_authority_unprotected.yaml @@ -0,0 +1,38 @@ +--- + +- name: Create private key for intermediate CA + community.crypto.openssl_privatekey: + path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + type: "{{ certificate_authority_intermediate_ca_tls_key_type }}" + +- name: Create a certificate signing request (CSR) for intermediate CA + community.crypto.openssl_csr: + basic_constraints: + - "CA:TRUE" + common_name: "{{ certificate_authority_intermediate_ca_common_name }}" + path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" + use_common_name_for_san: false + +- name: Create signed client certificate - unprotected root Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_intermediate_ca_not_after }}" + ownca_not_before: "{{ certificate_authority_intermediate_ca_not_before }}" + ownca_path: "{{ certificate_authority_root_ca_path }}/cert.pem" + ownca_privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + provider: ownca + when: certificate_authority_root_ca_tls_key_passphrase | length <= 0 + +- name: Create signed client certificate - passphrase protected root Certificate Authority (CA) + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" + ownca_not_after: "{{ certificate_authority_intermediate_ca_not_after }}" + ownca_not_before: "{{ certificate_authority_intermediate_ca_not_before }}" + ownca_path: "{{ certificate_authority_root_ca_path }}/cert.pem" + ownca_privatekey_passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}" + ownca_privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + path: "{{ certificate_authority_intermediate_ca_path }}/cert.pem" + provider: ownca + when: certificate_authority_root_ca_tls_key_passphrase | length > 0 diff --git a/tasks/main.yaml b/tasks/main.yaml new file mode 100644 index 0000000..810e2b3 --- /dev/null +++ b/tasks/main.yaml @@ -0,0 +1,26 @@ +--- + +- name: Upgrade python package manager pip + ansible.builtin.pip: + name: pip + state: latest + +- name: Install required python library cryptography + ansible.builtin.pip: + name: cryptography>=1.2.3 + state: present + +- name: Create or import a root Certificate Authority (CA) + ansible.builtin.include_tasks: root_certificate_authority.yaml + when: certificate_authority_root_ca_skip is defined and + not certificate_authority_root_ca_skip + +- name: Create or import a intermediate Certificate Authority (CA) + ansible.builtin.include_tasks: intermediate_certificate_authority.yaml + when: certificate_authority_intermediate_ca_skip is defined and + not certificate_authority_intermediate_ca_skip + +- name: Create or import a client certificate + ansible.builtin.include_tasks: client_certificate.yaml + when: certificate_authority_client_skip is defined and + not certificate_authority_client_skip diff --git a/tasks/root_certificate_authority.yaml b/tasks/root_certificate_authority.yaml new file mode 100644 index 0000000..57c39aa --- /dev/null +++ b/tasks/root_certificate_authority.yaml @@ -0,0 +1,84 @@ +--- + +- name: Create directory to store tls keys and certificates of the root CA + ansible.builtin.file: + path: "{{ certificate_authority_root_ca_path }}" + owner: "root" + group: "root" + mode: "0700" + state: "directory" + +- name: Create unprotected root Certificate Authority (CA) + ansible.builtin.include_tasks: root_certificate_authority_unprotected.yaml + when: certificate_authority_root_ca_create is defined and + certificate_authority_root_ca_create and + certificate_authority_root_ca_tls_key_passphrase is defined and + certificate_authority_root_ca_tls_key_passphrase | length <= 0 + +- name: Create passphrase protected root Certificate Authority (CA) + ansible.builtin.include_tasks: root_certificate_authority_unprotected.yaml + when: certificate_authority_root_ca_create is defined and + certificate_authority_root_ca_create and + certificate_authority_root_ca_tls_key_passphrase is defined and + certificate_authority_root_ca_tls_key_passphrase | length > 0 + +- name: Import protected root Certificate Authority (CA) + ansible.builtin.include_tasks: root_certificate_authority_import.yaml + when: certificate_authority_root_ca_create is defined and + not certificate_authority_root_ca_create + +- name: Create symbolic link for signed root certificate + ansible.builtin.file: + src: "{{ certificate_authority_root_ca_path }}/cert.pem" + dest: "{{ certificate_authority_root_ca_path }}/{{ item }}" + state: link + with_items: + - ca.pem + - chain.pem + - fullchain.pem + +- name: Create file with private key and fullchain file of root Certificate Authority (CA) + block: + - name: Check if private key exists + ansible.builtin.stat: + path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + register: _stat_result + - name: Concatenate private key and fullchain file of root Certificate Authority (CA) + vars: + _chain_files: + - "{{ certificate_authority_root_ca_path }}/privkey.pem" + - "{{ certificate_authority_root_ca_path }}/fullchain.pem" + ansible.builtin.command: + cmd: awk 1 {{ _chain_files | join(' ') }} + register: chain_content + changed_when: chain_content.rc == 0 + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + - name: Create concatenated file + ansible.builtin.copy: + content: "{{ chain_content.stdout_lines | join('\n') }}" + dest: "{{ certificate_authority_root_ca_path }}/all.pem" + owner: "root" + group: "root" + mode: "0600" + remote_src: true + when: _stat_result.stat.exists is defined and + _stat_result.stat.exists + +- name: Import certificate of root Certificate Authority (CA) into systems trust store + when: certificate_authority_root_ca_import is defined and + certificate_authority_root_ca_import + block: + - name: Create symolic link + ansible.builtin.file: + src: "{{ certificate_authority_root_ca_path }}/cert.pem" + dest: "/etc/pki/ca-trust/source/anchors/{{ certificate_authority_root_ca_common_name | replace(' ', '_') }}.pem" + owner: root + group: root + state: link + - name: Update systems SSL/TLS trust store + ansible.builtin.command: + cmd: /usr/bin/update-ca-trust + register: _update_ca_trust + changed_when: _update_ca_trust.rc == 0 + failed_when: _update_ca_trust.rc > 0 diff --git a/tasks/root_certificate_authority_import.yaml b/tasks/root_certificate_authority_import.yaml new file mode 100644 index 0000000..5b64b50 --- /dev/null +++ b/tasks/root_certificate_authority_import.yaml @@ -0,0 +1,19 @@ +--- + +- name: Import private key of root Certificate Authority (CA) + ansible.builtin.copy: + content: "{{ certificate_authority_root_ca_tls_key_content }}" + dest: "{{ certificate_authority_root_ca_path }}/privkey.pem" + owner: root + group: root + mode: "0600" + when: certificate_authority_root_ca_tls_key_content | length > 0 + +- name: Import certificate of root Certificate Authority (CA) + ansible.builtin.copy: + content: "{{ certificate_authority_root_ca_tls_crt_content }}" + dest: "{{ certificate_authority_root_ca_path }}/cert.pem" + owner: "root" + group: "root" + mode: "0644" + when: certificate_authority_root_ca_tls_crt_content | length > 0 diff --git a/tasks/root_certificate_authority_protected.yaml b/tasks/root_certificate_authority_protected.yaml new file mode 100644 index 0000000..67b7a4c --- /dev/null +++ b/tasks/root_certificate_authority_protected.yaml @@ -0,0 +1,26 @@ +--- + +- name: Create private key for root CA + community.crypto.openssl_privatekey: + passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}" + path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + type: "{{ certificate_authority_root_ca_tls_key_type }}" + +- name: Create a certificate signing request (CSR) for root CA + community.crypto.openssl_csr: + basic_constraints: + - "CA:TRUE" + common_name: "{{ certificate_authority_root_ca_common_name }}" + path: "{{ certificate_authority_root_ca_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + use_common_name_for_san: false + +- name: Create self-signed certificate for root CA + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_root_ca_path }}/cert-req.pem" + path: "{{ certificate_authority_root_ca_path }}/cert.pem" + privatekey_passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}" + privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + provider: selfsigned + selfsigned_not_after: "{{ certificate_authority_root_ca_not_after }}" + selfsigned_not_before: "{{ certificate_authority_root_ca_not_before }}" diff --git a/tasks/root_certificate_authority_unprotected.yaml b/tasks/root_certificate_authority_unprotected.yaml new file mode 100644 index 0000000..cde9d22 --- /dev/null +++ b/tasks/root_certificate_authority_unprotected.yaml @@ -0,0 +1,24 @@ +--- + +- name: Create private key for root CA + community.crypto.openssl_privatekey: + path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + type: "{{ certificate_authority_root_ca_tls_key_type }}" + +- name: Create a certificate signing request (CSR) for root CA + community.crypto.openssl_csr: + basic_constraints: + - "CA:TRUE" + common_name: "{{ certificate_authority_root_ca_common_name }}" + path: "{{ certificate_authority_root_ca_path }}/cert-req.pem" + privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + use_common_name_for_san: false + +- name: Create self-signed certificate for root CA + community.crypto.x509_certificate: + csr_path: "{{ certificate_authority_root_ca_path }}/cert-req.pem" + path: "{{ certificate_authority_root_ca_path }}/cert.pem" + privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" + provider: selfsigned + selfsigned_not_after: "{{ certificate_authority_root_ca_not_after }}" + selfsigned_not_before: "{{ certificate_authority_root_ca_not_before }}"