@valkyrianlabs/payload-markdown-docs 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +79 -171
  2. package/dist/admin/DocsSetManager.js +5 -3
  3. package/dist/admin/DocsSetManager.js.map +1 -1
  4. package/dist/admin/docsSetManagerData.d.ts +6 -5
  5. package/dist/admin/docsSetManagerData.js +60 -33
  6. package/dist/admin/docsSetManagerData.js.map +1 -1
  7. package/dist/admin/docsSetManagerTypes.d.ts +12 -9
  8. package/dist/admin/docsSetManagerTypes.js.map +1 -1
  9. package/dist/cli/commands/manifest.js +1 -2
  10. package/dist/cli/commands/manifest.js.map +1 -1
  11. package/dist/cli/commands/plan.js +1 -2
  12. package/dist/cli/commands/plan.js.map +1 -1
  13. package/dist/cli/commands/push.js +19 -12
  14. package/dist/cli/commands/push.js.map +1 -1
  15. package/dist/cli/commands/validate.js +11 -6
  16. package/dist/cli/commands/validate.js.map +1 -1
  17. package/dist/cli/index.js +6 -15
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/cli/parseArgs.js +0 -3
  20. package/dist/cli/parseArgs.js.map +1 -1
  21. package/dist/cli/types.d.ts +0 -3
  22. package/dist/cli/types.js.map +1 -1
  23. package/dist/collections/docs.js +0 -24
  24. package/dist/collections/docs.js.map +1 -1
  25. package/dist/collections/docsGroups.js +8 -9
  26. package/dist/collections/docsGroups.js.map +1 -1
  27. package/dist/collections/docsKeys.d.ts +5 -0
  28. package/dist/collections/docsKeys.js +44 -0
  29. package/dist/collections/docsKeys.js.map +1 -0
  30. package/dist/collections/docsSets.js +47 -202
  31. package/dist/collections/docsSets.js.map +1 -1
  32. package/dist/collections/docsTrusted.d.ts +5 -0
  33. package/dist/collections/docsTrusted.js +60 -0
  34. package/dist/collections/docsTrusted.js.map +1 -0
  35. package/dist/collections/index.d.ts +4 -0
  36. package/dist/collections/index.js +2 -0
  37. package/dist/collections/index.js.map +1 -1
  38. package/dist/constants.d.ts +3 -1
  39. package/dist/constants.js +3 -1
  40. package/dist/constants.js.map +1 -1
  41. package/dist/endpoints/sync.d.ts +6 -7
  42. package/dist/endpoints/sync.js +57 -124
  43. package/dist/endpoints/sync.js.map +1 -1
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.js +1 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/next/PayloadMarkdownDocsPage.js +2 -6
  48. package/dist/next/PayloadMarkdownDocsPage.js.map +1 -1
  49. package/dist/next/index.d.ts +2 -0
  50. package/dist/next/index.js +1 -0
  51. package/dist/next/index.js.map +1 -1
  52. package/dist/next/links.d.ts +11 -0
  53. package/dist/next/links.js +79 -0
  54. package/dist/next/links.js.map +1 -0
  55. package/dist/next/markdown.js +91 -19
  56. package/dist/next/markdown.js.map +1 -1
  57. package/dist/next/metadata.js +6 -6
  58. package/dist/next/metadata.js.map +1 -1
  59. package/dist/next/records.js +13 -23
  60. package/dist/next/records.js.map +1 -1
  61. package/dist/next/route.js +141 -49
  62. package/dist/next/route.js.map +1 -1
  63. package/dist/next/types.d.ts +0 -14
  64. package/dist/next/types.js.map +1 -1
  65. package/dist/payload/docsKeys.d.ts +20 -0
  66. package/dist/payload/docsKeys.js +29 -0
  67. package/dist/payload/docsKeys.js.map +1 -0
  68. package/dist/payload/docsSets.d.ts +32 -6
  69. package/dist/payload/docsSets.js +146 -83
  70. package/dist/payload/docsSets.js.map +1 -1
  71. package/dist/payload/docsTrusted.d.ts +16 -0
  72. package/dist/payload/docsTrusted.js +49 -0
  73. package/dist/payload/docsTrusted.js.map +1 -0
  74. package/dist/payload/index.d.ts +5 -1
  75. package/dist/payload/index.js +3 -1
  76. package/dist/payload/index.js.map +1 -1
  77. package/dist/plugin.js +36 -9
  78. package/dist/plugin.js.map +1 -1
  79. package/dist/security/ed25519Keys.d.ts +9 -0
  80. package/dist/security/ed25519Keys.js +183 -0
  81. package/dist/security/ed25519Keys.js.map +1 -0
  82. package/dist/security/githubOidc.d.ts +18 -5
  83. package/dist/security/githubOidc.js +44 -16
  84. package/dist/security/githubOidc.js.map +1 -1
  85. package/dist/security/index.d.ts +2 -1
  86. package/dist/security/index.js +1 -0
  87. package/dist/security/index.js.map +1 -1
  88. package/dist/security/sign.js +3 -12
  89. package/dist/security/sign.js.map +1 -1
  90. package/dist/security/verify.js +3 -12
  91. package/dist/security/verify.js.map +1 -1
  92. package/dist/skills/codex/SKILL.md +3 -4
  93. package/dist/skills/codex/examples/github-actions.md +0 -2
  94. package/dist/skills/codex/reference/admin.md +0 -6
  95. package/dist/skills/codex/reference/routing.md +2 -1
  96. package/dist/skills/codex/reference/sync.md +7 -5
  97. package/dist/skills/codex/reference/troubleshooting.md +3 -4
  98. package/dist/skills/codex/reference/workflow.md +0 -1
  99. package/dist/sync/manifest.d.ts +1 -3
  100. package/dist/sync/manifest.js +2 -3
  101. package/dist/sync/manifest.js.map +1 -1
  102. package/dist/sync/validate.js +1 -2
  103. package/dist/sync/validate.js.map +1 -1
  104. package/dist/types.d.ts +7 -50
  105. package/dist/types.js.map +1 -1
  106. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Config, Plugin } from 'payload'\n\nimport type { PayloadMarkdownDocsConfig } from './types.js'\n\nimport {\n createDocsCollection,\n createDocsGroupsCollection,\n createDocsSetsCollection,\n createNoncesCollection,\n createSyncRunsCollection,\n} from './collections/index.js'\nimport {\n DEFAULT_DOCS_COLLECTION_SLUG,\n DEFAULT_DOCS_GROUPS_COLLECTION_SLUG,\n DEFAULT_DOCS_ROUTE_BASE,\n DEFAULT_DOCS_SETS_COLLECTION_SLUG,\n DEFAULT_DOCS_SYNC_ENDPOINT_PATH,\n DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG,\n DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG,\n DEFAULT_MARKDOWN_FIELD_NAME,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_PAGES_BRIDGE_FIELD,\n DEFAULT_PAGES_COLLECTION_SLUG,\n DEFAULT_PAGES_ROUTE_FIELD,\n} from './constants.js'\nimport { createSyncEndpoint } from './endpoints/index.js'\n\ntype ResolvedCollectionOptions = {\n docsCollectionSlug: string\n docsEnabled: boolean\n docsGroupsCollectionSlug: string\n docsGroupsEnabled: boolean\n docsSetsCollectionSlug: string\n docsSetsEnabled: boolean\n enableDrafts: boolean\n markdownFieldName: string\n noncesCollectionSlug: string\n noncesEnabled: boolean\n syncRunsCollectionSlug: string\n syncRunsEnabled: boolean\n}\n\nconst normalizeEndpointPath = (path: string): string => {\n const normalized = `/${path.trim()}`.replace(/\\/+/g, '/')\n\n return normalized.length > 1 ? normalized.replace(/\\/+$/g, '') : normalized\n}\n\nconst resolveCollectionOptions = (\n pluginOptions: PayloadMarkdownDocsConfig,\n): ResolvedCollectionOptions => {\n if (pluginOptions.target?.type === 'existingCollection') {\n throw new Error(\n 'payloadMarkdownDocs: target.type \"existingCollection\" is not supported yet. Use target.type \"docsCollection\".',\n )\n }\n\n const docsSlugFromTarget = pluginOptions.target?.slug\n const docsSlugFromCollections = pluginOptions.collections?.docs?.slug\n\n if (\n docsSlugFromTarget &&\n docsSlugFromCollections &&\n docsSlugFromTarget !== docsSlugFromCollections\n ) {\n throw new Error(\n 'payloadMarkdownDocs: target.slug and collections.docs.slug must match when both are provided.',\n )\n }\n\n return {\n docsCollectionSlug:\n docsSlugFromTarget ?? docsSlugFromCollections ?? DEFAULT_DOCS_COLLECTION_SLUG,\n docsEnabled: pluginOptions.collections?.docs?.enabled !== false,\n docsGroupsCollectionSlug:\n pluginOptions.collections?.docsGroups?.slug ?? DEFAULT_DOCS_GROUPS_COLLECTION_SLUG,\n docsGroupsEnabled: pluginOptions.collections?.docsGroups?.enabled !== false,\n docsSetsCollectionSlug:\n pluginOptions.collections?.docsSets?.slug ?? DEFAULT_DOCS_SETS_COLLECTION_SLUG,\n docsSetsEnabled: pluginOptions.collections?.docsSets?.enabled !== false,\n enableDrafts:\n pluginOptions.target?.type === 'docsCollection'\n ? pluginOptions.target.enableDrafts === true\n : false,\n markdownFieldName:\n pluginOptions.target?.type === 'docsCollection'\n ? pluginOptions.target.markdownField ?? DEFAULT_MARKDOWN_FIELD_NAME\n : DEFAULT_MARKDOWN_FIELD_NAME,\n noncesCollectionSlug:\n pluginOptions.collections?.nonces?.slug ?? DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG,\n noncesEnabled: pluginOptions.collections?.nonces?.enabled !== false,\n syncRunsCollectionSlug:\n pluginOptions.collections?.syncRuns?.slug ?? DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG,\n syncRunsEnabled: pluginOptions.collections?.syncRuns?.enabled !== false,\n }\n}\n\nconst assertCollectionOptionCompatibility = ({\n docsGroupsEnabled,\n docsSetsEnabled,\n}: ResolvedCollectionOptions) => {\n if (docsSetsEnabled && !docsGroupsEnabled) {\n throw new Error(\n 'payloadMarkdownDocs: collections.docsSets requires collections.docsGroups to be enabled.',\n )\n }\n}\n\nconst assertNoCollectionSlugConflicts = (\n incomingConfig: Config,\n collectionSlugsToAdd: string[],\n) => {\n const duplicateRequestedSlug = collectionSlugsToAdd.find(\n (slug, index) => collectionSlugsToAdd.indexOf(slug) !== index,\n )\n\n if (duplicateRequestedSlug) {\n throw new Error(\n `payloadMarkdownDocs: collection slug \"${duplicateRequestedSlug}\" is configured more than once.`,\n )\n }\n\n const existingCollectionSlugs = new Set(\n incomingConfig.collections?.map((collection) => collection.slug) ?? [],\n )\n\n const conflictingSlug = collectionSlugsToAdd.find((slug) =>\n existingCollectionSlugs.has(slug),\n )\n\n if (conflictingSlug) {\n throw new Error(\n `payloadMarkdownDocs: collection slug \"${conflictingSlug}\" already exists in the Payload config.`,\n )\n }\n}\n\nexport const payloadMarkdownDocs =\n (pluginOptions: PayloadMarkdownDocsConfig = {}): Plugin =>\n (incomingConfig: Config): Config => {\n if (pluginOptions.enabled === false) {\n return incomingConfig\n }\n\n const {\n docsCollectionSlug,\n docsEnabled,\n docsGroupsCollectionSlug,\n docsGroupsEnabled,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n enableDrafts,\n markdownFieldName,\n noncesCollectionSlug,\n noncesEnabled,\n syncRunsCollectionSlug,\n syncRunsEnabled,\n } = resolveCollectionOptions(pluginOptions)\n assertCollectionOptionCompatibility({\n docsCollectionSlug,\n docsEnabled,\n docsGroupsCollectionSlug,\n docsGroupsEnabled,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n enableDrafts,\n markdownFieldName,\n noncesCollectionSlug,\n noncesEnabled,\n syncRunsCollectionSlug,\n syncRunsEnabled,\n })\n const endpointPath = normalizeEndpointPath(\n pluginOptions.endpoint?.path ?? DEFAULT_DOCS_SYNC_ENDPOINT_PATH,\n )\n\n const collectionSlugsToAdd = [\n ...(docsGroupsEnabled ? [docsGroupsCollectionSlug] : []),\n ...(docsSetsEnabled ? [docsSetsCollectionSlug] : []),\n ...(docsEnabled ? [docsCollectionSlug] : []),\n ...(syncRunsEnabled ? [syncRunsCollectionSlug] : []),\n ...(noncesEnabled ? [noncesCollectionSlug] : []),\n ]\n\n assertNoCollectionSlugConflicts(incomingConfig, collectionSlugsToAdd)\n\n const addedCollections = [\n ...(docsGroupsEnabled\n ? [\n createDocsGroupsCollection({\n slug: docsGroupsCollectionSlug,\n }),\n ]\n : []),\n ...(docsSetsEnabled\n ? [\n createDocsSetsCollection({\n slug: docsSetsCollectionSlug,\n docsCollectionSlug: docsEnabled ? docsCollectionSlug : undefined,\n docsGroupsCollectionSlug,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ...(docsEnabled\n ? [\n createDocsCollection({\n slug: docsCollectionSlug,\n docsSetsCollectionSlug: docsSetsEnabled\n ? docsSetsCollectionSlug\n : undefined,\n enableDrafts,\n markdownFieldName,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ...(syncRunsEnabled\n ? [\n createSyncRunsCollection({\n slug: syncRunsCollectionSlug,\n }),\n ]\n : []),\n ...(noncesEnabled\n ? [\n createNoncesCollection({\n slug: noncesCollectionSlug,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ]\n\n return {\n ...incomingConfig,\n collections: [...(incomingConfig.collections ?? []), ...addedCollections],\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n createSyncEndpoint({\n allowHardDelete: pluginOptions.sync?.allowHardDelete,\n allowPublish: pluginOptions.sync?.allowPublish,\n allowWrites: pluginOptions.sync?.allowWrites,\n auth: pluginOptions.auth,\n defaultPublishMode: pluginOptions.sync?.defaultPublishMode,\n deleteBehavior: pluginOptions.sync?.deleteBehavior,\n docsCollectionSlug,\n docsEnabled,\n docsEnableDrafts: enableDrafts,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n endpointPath,\n markdownFieldName,\n maxBodyBytes: pluginOptions.endpoint?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n noncesCollectionSlug,\n noncesEnabled,\n requireDryRunBeforeApply: pluginOptions.sync?.requireDryRunBeforeApply,\n routeBase: DEFAULT_DOCS_ROUTE_BASE,\n routing: {\n pages: {\n allowBridgePages:\n pluginOptions.routing?.pages?.allowBridgePages ?? true,\n bridgeField:\n pluginOptions.routing?.pages?.bridgeField ?? DEFAULT_PAGES_BRIDGE_FIELD,\n collection:\n pluginOptions.routing?.pages?.collection ?? DEFAULT_PAGES_COLLECTION_SLUG,\n enabled: pluginOptions.routing?.pages?.enabled === true,\n routeField:\n pluginOptions.routing?.pages?.routeField ?? DEFAULT_PAGES_ROUTE_FIELD,\n },\n },\n sources: pluginOptions.sources,\n syncRunsCollectionSlug,\n syncRunsEnabled,\n }),\n ],\n }\n }\n"],"names":["createDocsCollection","createDocsGroupsCollection","createDocsSetsCollection","createNoncesCollection","createSyncRunsCollection","DEFAULT_DOCS_COLLECTION_SLUG","DEFAULT_DOCS_GROUPS_COLLECTION_SLUG","DEFAULT_DOCS_ROUTE_BASE","DEFAULT_DOCS_SETS_COLLECTION_SLUG","DEFAULT_DOCS_SYNC_ENDPOINT_PATH","DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG","DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG","DEFAULT_MARKDOWN_FIELD_NAME","DEFAULT_MAX_BODY_BYTES","DEFAULT_PAGES_BRIDGE_FIELD","DEFAULT_PAGES_COLLECTION_SLUG","DEFAULT_PAGES_ROUTE_FIELD","createSyncEndpoint","normalizeEndpointPath","path","normalized","trim","replace","length","resolveCollectionOptions","pluginOptions","target","type","Error","docsSlugFromTarget","slug","docsSlugFromCollections","collections","docs","docsCollectionSlug","docsEnabled","enabled","docsGroupsCollectionSlug","docsGroups","docsGroupsEnabled","docsSetsCollectionSlug","docsSets","docsSetsEnabled","enableDrafts","markdownFieldName","markdownField","noncesCollectionSlug","nonces","noncesEnabled","syncRunsCollectionSlug","syncRuns","syncRunsEnabled","assertCollectionOptionCompatibility","assertNoCollectionSlugConflicts","incomingConfig","collectionSlugsToAdd","duplicateRequestedSlug","find","index","indexOf","existingCollectionSlugs","Set","map","collection","conflictingSlug","has","payloadMarkdownDocs","endpointPath","endpoint","addedCollections","undefined","endpoints","allowHardDelete","sync","allowPublish","allowWrites","auth","defaultPublishMode","deleteBehavior","docsEnableDrafts","maxBodyBytes","requireDryRunBeforeApply","routeBase","routing","pages","allowBridgePages","bridgeField","routeField","sources"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Config, Plugin } from 'payload'\n\nimport type { PayloadMarkdownDocsConfig } from './types.js'\n\nimport {\n createDocsCollection,\n createDocsGroupsCollection,\n createDocsKeysCollection,\n createDocsSetsCollection,\n createDocsTrustedCollection,\n createNoncesCollection,\n createSyncRunsCollection,\n} from './collections/index.js'\nimport {\n DEFAULT_DOCS_COLLECTION_SLUG,\n DEFAULT_DOCS_GROUPS_COLLECTION_SLUG,\n DEFAULT_DOCS_KEYS_COLLECTION_SLUG,\n DEFAULT_DOCS_SETS_COLLECTION_SLUG,\n DEFAULT_DOCS_SYNC_ENDPOINT_PATH,\n DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG,\n DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG,\n DEFAULT_DOCS_TRUSTED_COLLECTION_SLUG,\n DEFAULT_MARKDOWN_FIELD_NAME,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_PAGES_BRIDGE_FIELD,\n DEFAULT_PAGES_COLLECTION_SLUG,\n DEFAULT_PAGES_ROUTE_FIELD,\n} from './constants.js'\nimport { createSyncEndpoint } from './endpoints/index.js'\n\ntype ResolvedCollectionOptions = {\n docsCollectionSlug: string\n docsEnabled: boolean\n docsGroupsCollectionSlug: string\n docsGroupsEnabled: boolean\n docsKeysCollectionSlug: string\n docsKeysEnabled: boolean\n docsSetsCollectionSlug: string\n docsSetsEnabled: boolean\n docsTrustedCollectionSlug: string\n docsTrustedEnabled: boolean\n enableDrafts: boolean\n markdownFieldName: string\n noncesCollectionSlug: string\n noncesEnabled: boolean\n syncRunsCollectionSlug: string\n syncRunsEnabled: boolean\n}\n\nconst normalizeEndpointPath = (path: string): string => {\n const normalized = `/${path.trim()}`.replace(/\\/+/g, '/')\n\n return normalized.length > 1 ? normalized.replace(/\\/+$/g, '') : normalized\n}\n\nconst resolveCollectionOptions = (\n pluginOptions: PayloadMarkdownDocsConfig,\n): ResolvedCollectionOptions => {\n if (\n pluginOptions.target?.type !== undefined &&\n pluginOptions.target.type !== 'docsCollection'\n ) {\n throw new Error(\n 'payloadMarkdownDocs: target.type only supports \"docsCollection\". existingCollection is not supported.',\n )\n }\n\n const docsSlugFromTarget = pluginOptions.target?.slug\n const docsSlugFromCollections = pluginOptions.collections?.docs?.slug\n\n if (\n docsSlugFromTarget &&\n docsSlugFromCollections &&\n docsSlugFromTarget !== docsSlugFromCollections\n ) {\n throw new Error(\n 'payloadMarkdownDocs: target.slug and collections.docs.slug must match when both are provided.',\n )\n }\n\n return {\n docsCollectionSlug:\n docsSlugFromTarget ?? docsSlugFromCollections ?? DEFAULT_DOCS_COLLECTION_SLUG,\n docsEnabled: pluginOptions.collections?.docs?.enabled !== false,\n docsGroupsCollectionSlug:\n pluginOptions.collections?.docsGroups?.slug ?? DEFAULT_DOCS_GROUPS_COLLECTION_SLUG,\n docsGroupsEnabled: pluginOptions.collections?.docsGroups?.enabled !== false,\n docsKeysCollectionSlug:\n pluginOptions.collections?.docsKeys?.slug ?? DEFAULT_DOCS_KEYS_COLLECTION_SLUG,\n docsKeysEnabled: pluginOptions.collections?.docsKeys?.enabled !== false,\n docsSetsCollectionSlug:\n pluginOptions.collections?.docsSets?.slug ?? DEFAULT_DOCS_SETS_COLLECTION_SLUG,\n docsSetsEnabled: pluginOptions.collections?.docsSets?.enabled !== false,\n docsTrustedCollectionSlug:\n pluginOptions.collections?.docsTrusted?.slug ?? DEFAULT_DOCS_TRUSTED_COLLECTION_SLUG,\n docsTrustedEnabled: pluginOptions.collections?.docsTrusted?.enabled !== false,\n enableDrafts:\n pluginOptions.target?.enableDrafts === true,\n markdownFieldName:\n pluginOptions.target?.markdownField ?? DEFAULT_MARKDOWN_FIELD_NAME,\n noncesCollectionSlug:\n pluginOptions.collections?.nonces?.slug ?? DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG,\n noncesEnabled: pluginOptions.collections?.nonces?.enabled !== false,\n syncRunsCollectionSlug:\n pluginOptions.collections?.syncRuns?.slug ?? DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG,\n syncRunsEnabled: pluginOptions.collections?.syncRuns?.enabled !== false,\n }\n}\n\nconst assertCollectionOptionCompatibility = ({\n docsGroupsEnabled,\n docsSetsEnabled,\n}: ResolvedCollectionOptions) => {\n if (docsSetsEnabled && !docsGroupsEnabled) {\n throw new Error(\n 'payloadMarkdownDocs: collections.docsSets requires collections.docsGroups to be enabled.',\n )\n }\n}\n\nconst assertNoCollectionSlugConflicts = (\n incomingConfig: Config,\n collectionSlugsToAdd: string[],\n) => {\n const duplicateRequestedSlug = collectionSlugsToAdd.find(\n (slug, index) => collectionSlugsToAdd.indexOf(slug) !== index,\n )\n\n if (duplicateRequestedSlug) {\n throw new Error(\n `payloadMarkdownDocs: collection slug \"${duplicateRequestedSlug}\" is configured more than once.`,\n )\n }\n\n const existingCollectionSlugs = new Set(\n incomingConfig.collections?.map((collection) => collection.slug) ?? [],\n )\n\n const conflictingSlug = collectionSlugsToAdd.find((slug) =>\n existingCollectionSlugs.has(slug),\n )\n\n if (conflictingSlug) {\n throw new Error(\n `payloadMarkdownDocs: collection slug \"${conflictingSlug}\" already exists in the Payload config.`,\n )\n }\n}\n\nexport const payloadMarkdownDocs =\n (pluginOptions: PayloadMarkdownDocsConfig = {}): Plugin =>\n (incomingConfig: Config): Config => {\n if (pluginOptions.enabled === false) {\n return incomingConfig\n }\n\n const {\n docsCollectionSlug,\n docsEnabled,\n docsGroupsCollectionSlug,\n docsGroupsEnabled,\n docsKeysCollectionSlug,\n docsKeysEnabled,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n docsTrustedCollectionSlug,\n docsTrustedEnabled,\n enableDrafts,\n markdownFieldName,\n noncesCollectionSlug,\n noncesEnabled,\n syncRunsCollectionSlug,\n syncRunsEnabled,\n } = resolveCollectionOptions(pluginOptions)\n assertCollectionOptionCompatibility({\n docsCollectionSlug,\n docsEnabled,\n docsGroupsCollectionSlug,\n docsGroupsEnabled,\n docsKeysCollectionSlug,\n docsKeysEnabled,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n docsTrustedCollectionSlug,\n docsTrustedEnabled,\n enableDrafts,\n markdownFieldName,\n noncesCollectionSlug,\n noncesEnabled,\n syncRunsCollectionSlug,\n syncRunsEnabled,\n })\n const endpointPath = normalizeEndpointPath(\n pluginOptions.endpoint?.path ?? DEFAULT_DOCS_SYNC_ENDPOINT_PATH,\n )\n\n const collectionSlugsToAdd = [\n ...(docsGroupsEnabled ? [docsGroupsCollectionSlug] : []),\n ...(docsSetsEnabled ? [docsSetsCollectionSlug] : []),\n ...(docsKeysEnabled ? [docsKeysCollectionSlug] : []),\n ...(docsTrustedEnabled ? [docsTrustedCollectionSlug] : []),\n ...(docsEnabled ? [docsCollectionSlug] : []),\n ...(syncRunsEnabled ? [syncRunsCollectionSlug] : []),\n ...(noncesEnabled ? [noncesCollectionSlug] : []),\n ]\n\n assertNoCollectionSlugConflicts(incomingConfig, collectionSlugsToAdd)\n\n const addedCollections = [\n ...(docsGroupsEnabled\n ? [\n createDocsGroupsCollection({\n slug: docsGroupsCollectionSlug,\n }),\n ]\n : []),\n ...(docsSetsEnabled\n ? [\n createDocsSetsCollection({\n slug: docsSetsCollectionSlug,\n docsCollectionSlug: docsEnabled ? docsCollectionSlug : undefined,\n docsGroupsCollectionSlug,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ...(docsKeysEnabled\n ? [\n createDocsKeysCollection({\n slug: docsKeysCollectionSlug,\n }),\n ]\n : []),\n ...(docsTrustedEnabled\n ? [\n createDocsTrustedCollection({\n slug: docsTrustedCollectionSlug,\n }),\n ]\n : []),\n ...(docsEnabled\n ? [\n createDocsCollection({\n slug: docsCollectionSlug,\n docsSetsCollectionSlug: docsSetsEnabled\n ? docsSetsCollectionSlug\n : undefined,\n enableDrafts,\n markdownFieldName,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ...(syncRunsEnabled\n ? [\n createSyncRunsCollection({\n slug: syncRunsCollectionSlug,\n }),\n ]\n : []),\n ...(noncesEnabled\n ? [\n createNoncesCollection({\n slug: noncesCollectionSlug,\n syncRunsCollectionSlug: syncRunsEnabled ? syncRunsCollectionSlug : undefined,\n }),\n ]\n : []),\n ]\n\n return {\n ...incomingConfig,\n collections: [...(incomingConfig.collections ?? []), ...addedCollections],\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n createSyncEndpoint({\n allowHardDelete: pluginOptions.sync?.allowHardDelete,\n allowPublish: pluginOptions.sync?.allowPublish,\n allowWrites: pluginOptions.sync?.allowWrites,\n auth: pluginOptions.auth,\n defaultPublishMode: pluginOptions.sync?.defaultPublishMode,\n deleteBehavior: pluginOptions.sync?.deleteBehavior,\n docsCollectionSlug,\n docsEnabled,\n docsEnableDrafts: enableDrafts,\n docsGroupsCollectionSlug,\n docsKeysCollectionSlug,\n docsKeysEnabled,\n docsSetsCollectionSlug,\n docsSetsEnabled,\n docsTrustedCollectionSlug,\n docsTrustedEnabled,\n endpointPath,\n markdownFieldName,\n maxBodyBytes: pluginOptions.endpoint?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n noncesCollectionSlug,\n noncesEnabled,\n requireDryRunBeforeApply: pluginOptions.sync?.requireDryRunBeforeApply,\n routing: {\n pages: {\n allowBridgePages:\n pluginOptions.routing?.pages?.allowBridgePages ?? true,\n bridgeField:\n pluginOptions.routing?.pages?.bridgeField ?? DEFAULT_PAGES_BRIDGE_FIELD,\n collection:\n pluginOptions.routing?.pages?.collection ?? DEFAULT_PAGES_COLLECTION_SLUG,\n enabled: pluginOptions.routing?.pages?.enabled === true,\n routeField:\n pluginOptions.routing?.pages?.routeField ?? DEFAULT_PAGES_ROUTE_FIELD,\n },\n },\n syncRunsCollectionSlug,\n syncRunsEnabled,\n }),\n ],\n }\n }\n"],"names":["createDocsCollection","createDocsGroupsCollection","createDocsKeysCollection","createDocsSetsCollection","createDocsTrustedCollection","createNoncesCollection","createSyncRunsCollection","DEFAULT_DOCS_COLLECTION_SLUG","DEFAULT_DOCS_GROUPS_COLLECTION_SLUG","DEFAULT_DOCS_KEYS_COLLECTION_SLUG","DEFAULT_DOCS_SETS_COLLECTION_SLUG","DEFAULT_DOCS_SYNC_ENDPOINT_PATH","DEFAULT_DOCS_SYNC_NONCES_COLLECTION_SLUG","DEFAULT_DOCS_SYNC_RUNS_COLLECTION_SLUG","DEFAULT_DOCS_TRUSTED_COLLECTION_SLUG","DEFAULT_MARKDOWN_FIELD_NAME","DEFAULT_MAX_BODY_BYTES","DEFAULT_PAGES_BRIDGE_FIELD","DEFAULT_PAGES_COLLECTION_SLUG","DEFAULT_PAGES_ROUTE_FIELD","createSyncEndpoint","normalizeEndpointPath","path","normalized","trim","replace","length","resolveCollectionOptions","pluginOptions","target","type","undefined","Error","docsSlugFromTarget","slug","docsSlugFromCollections","collections","docs","docsCollectionSlug","docsEnabled","enabled","docsGroupsCollectionSlug","docsGroups","docsGroupsEnabled","docsKeysCollectionSlug","docsKeys","docsKeysEnabled","docsSetsCollectionSlug","docsSets","docsSetsEnabled","docsTrustedCollectionSlug","docsTrusted","docsTrustedEnabled","enableDrafts","markdownFieldName","markdownField","noncesCollectionSlug","nonces","noncesEnabled","syncRunsCollectionSlug","syncRuns","syncRunsEnabled","assertCollectionOptionCompatibility","assertNoCollectionSlugConflicts","incomingConfig","collectionSlugsToAdd","duplicateRequestedSlug","find","index","indexOf","existingCollectionSlugs","Set","map","collection","conflictingSlug","has","payloadMarkdownDocs","endpointPath","endpoint","addedCollections","endpoints","allowHardDelete","sync","allowPublish","allowWrites","auth","defaultPublishMode","deleteBehavior","docsEnableDrafts","maxBodyBytes","requireDryRunBeforeApply","routing","pages","allowBridgePages","bridgeField","routeField"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare class DocsSyncKeyError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare const getEd25519PrivateKeyInput: (privateKey: string) => import("crypto").KeyObject;
5
+ export declare const getEd25519PublicKeyInput: (publicKey: string) => string | import("crypto").KeyObject;
6
+ export declare const buildOpenSshEd25519PublicKey: ({ comment, publicKey, }: {
7
+ comment?: string;
8
+ publicKey: Buffer;
9
+ }) => string;
@@ -0,0 +1,183 @@
1
+ import { createPrivateKey, createPublicKey } from 'node:crypto';
2
+ const opensshPrivateKeyBegin = '-----BEGIN OPENSSH PRIVATE KEY-----';
3
+ const opensshPrivateKeyEnd = '-----END OPENSSH PRIVATE KEY-----';
4
+ const pkcs8Ed25519PrivateKeyDerPrefix = Buffer.from('302e020100300506032b657004220420', 'hex');
5
+ const spkiEd25519PublicKeyDerPrefix = Buffer.from('302a300506032b6570032100', 'hex');
6
+ class BufferReader {
7
+ buffer;
8
+ offset = 0;
9
+ constructor(buffer){
10
+ this.buffer = buffer;
11
+ }
12
+ readBytes(length) {
13
+ if (length < 0 || this.remaining < length) {
14
+ throw new DocsSyncKeyError('OpenSSH key data is truncated.');
15
+ }
16
+ const value = this.buffer.subarray(this.offset, this.offset + length);
17
+ this.offset += length;
18
+ return value;
19
+ }
20
+ readString() {
21
+ const length = this.readUInt32();
22
+ return this.readBytes(length);
23
+ }
24
+ readUInt32() {
25
+ if (this.remaining < 4) {
26
+ throw new DocsSyncKeyError('OpenSSH key data is truncated.');
27
+ }
28
+ const value = this.buffer.readUInt32BE(this.offset);
29
+ this.offset += 4;
30
+ return value;
31
+ }
32
+ get remaining() {
33
+ return this.buffer.length - this.offset;
34
+ }
35
+ }
36
+ export class DocsSyncKeyError extends Error {
37
+ constructor(message){
38
+ super(message);
39
+ this.name = 'DocsSyncKeyError';
40
+ }
41
+ }
42
+ const packOpenSshString = (value)=>{
43
+ const buffer = typeof value === 'string' ? Buffer.from(value, 'utf8') : value;
44
+ const length = Buffer.alloc(4);
45
+ length.writeUInt32BE(buffer.length, 0);
46
+ return Buffer.concat([
47
+ length,
48
+ buffer
49
+ ]);
50
+ };
51
+ const normalizeBase64 = (value)=>value.replace(/\s+/g, '');
52
+ const createEd25519PrivateKeyFromSeed = (seed)=>{
53
+ if (seed.length !== 32) {
54
+ throw new DocsSyncKeyError('OpenSSH Ed25519 private key seed is invalid.');
55
+ }
56
+ return createPrivateKey({
57
+ type: 'pkcs8',
58
+ format: 'der',
59
+ key: Buffer.concat([
60
+ pkcs8Ed25519PrivateKeyDerPrefix,
61
+ seed
62
+ ])
63
+ });
64
+ };
65
+ const createEd25519PublicKeyFromRaw = (publicKey)=>{
66
+ if (publicKey.length !== 32) {
67
+ throw new DocsSyncKeyError('OpenSSH Ed25519 public key is invalid.');
68
+ }
69
+ return createPublicKey({
70
+ type: 'spki',
71
+ format: 'der',
72
+ key: Buffer.concat([
73
+ spkiEd25519PublicKeyDerPrefix,
74
+ publicKey
75
+ ])
76
+ });
77
+ };
78
+ const parseOpenSshPrivateKey = (privateKey)=>{
79
+ const begin = privateKey.indexOf(opensshPrivateKeyBegin);
80
+ const end = privateKey.indexOf(opensshPrivateKeyEnd);
81
+ if (begin < 0 || end < 0 || end <= begin) {
82
+ throw new DocsSyncKeyError('OpenSSH private key PEM is invalid.');
83
+ }
84
+ const base64Body = privateKey.slice(begin + opensshPrivateKeyBegin.length, end);
85
+ const data = Buffer.from(normalizeBase64(base64Body), 'base64');
86
+ const authMagic = Buffer.from('openssh-key-v1\0', 'utf8');
87
+ if (!data.subarray(0, authMagic.length).equals(authMagic)) {
88
+ throw new DocsSyncKeyError('OpenSSH private key magic header is invalid.');
89
+ }
90
+ const reader = new BufferReader(data.subarray(authMagic.length));
91
+ const cipherName = reader.readString().toString('utf8');
92
+ const kdfName = reader.readString().toString('utf8');
93
+ reader.readString();
94
+ if (cipherName !== 'none' || kdfName !== 'none') {
95
+ throw new DocsSyncKeyError('Encrypted OpenSSH private keys are not supported. Use `payload-markdown-docs keygen --out .docs-sync` or provide an unencrypted PKCS#8 PEM Ed25519 private key.');
96
+ }
97
+ const keyCount = reader.readUInt32();
98
+ if (keyCount !== 1) {
99
+ throw new DocsSyncKeyError('OpenSSH private key must contain exactly one key.');
100
+ }
101
+ reader.readString();
102
+ const privateBlob = reader.readString();
103
+ const privateReader = new BufferReader(privateBlob);
104
+ const checkInt = privateReader.readUInt32();
105
+ const repeatedCheckInt = privateReader.readUInt32();
106
+ if (checkInt !== repeatedCheckInt) {
107
+ throw new DocsSyncKeyError('OpenSSH private key check values do not match.');
108
+ }
109
+ const keyType = privateReader.readString().toString('utf8');
110
+ if (keyType !== 'ssh-ed25519') {
111
+ throw new DocsSyncKeyError('Only Ed25519 private keys are supported for docs sync signing.');
112
+ }
113
+ const publicKey = privateReader.readString();
114
+ const privateKeyBytes = privateReader.readString();
115
+ if (privateKeyBytes.length !== 64) {
116
+ throw new DocsSyncKeyError('OpenSSH Ed25519 private key payload is invalid.');
117
+ }
118
+ if (!privateKeyBytes.subarray(32).equals(publicKey)) {
119
+ throw new DocsSyncKeyError('OpenSSH Ed25519 private/public key data does not match.');
120
+ }
121
+ return createEd25519PrivateKeyFromSeed(privateKeyBytes.subarray(0, 32));
122
+ };
123
+ const parseOpenSshPublicKey = (publicKey)=>{
124
+ const [keyType, base64Key] = publicKey.trim().split(/\s+/, 3);
125
+ if (keyType !== 'ssh-ed25519' || !base64Key) {
126
+ throw new DocsSyncKeyError('Only Ed25519 public keys are supported for docs sync verification.');
127
+ }
128
+ const reader = new BufferReader(Buffer.from(base64Key, 'base64'));
129
+ const parsedKeyType = reader.readString().toString('utf8');
130
+ if (parsedKeyType !== 'ssh-ed25519') {
131
+ throw new DocsSyncKeyError('OpenSSH public key type does not match ssh-ed25519.');
132
+ }
133
+ return createEd25519PublicKeyFromRaw(reader.readString());
134
+ };
135
+ export const getEd25519PrivateKeyInput = (privateKey)=>{
136
+ const trimmed = privateKey.trim();
137
+ if (trimmed.includes('BEGIN OPENSSH PRIVATE KEY')) {
138
+ return parseOpenSshPrivateKey(trimmed);
139
+ }
140
+ if (trimmed.includes('BEGIN')) {
141
+ try {
142
+ return createPrivateKey(trimmed);
143
+ } catch {
144
+ throw new DocsSyncKeyError('Private key must be an Ed25519 PKCS#8 PEM key, base64 PKCS#8 DER key, or unencrypted OpenSSH Ed25519 private key.');
145
+ }
146
+ }
147
+ try {
148
+ return createPrivateKey({
149
+ type: 'pkcs8',
150
+ format: 'der',
151
+ key: Buffer.from(normalizeBase64(trimmed), 'base64')
152
+ });
153
+ } catch {
154
+ throw new DocsSyncKeyError('Private key must be an Ed25519 PKCS#8 PEM key, base64 PKCS#8 DER key, or unencrypted OpenSSH Ed25519 private key.');
155
+ }
156
+ };
157
+ export const getEd25519PublicKeyInput = (publicKey)=>{
158
+ const trimmed = publicKey.trim();
159
+ if (trimmed.startsWith('ssh-ed25519 ')) {
160
+ return parseOpenSshPublicKey(trimmed);
161
+ }
162
+ if (trimmed.includes('BEGIN PUBLIC KEY')) {
163
+ return trimmed;
164
+ }
165
+ return createPublicKey({
166
+ type: 'spki',
167
+ format: 'der',
168
+ key: Buffer.from(normalizeBase64(trimmed), 'base64')
169
+ });
170
+ };
171
+ export const buildOpenSshEd25519PublicKey = ({ comment, publicKey })=>{
172
+ const blob = Buffer.concat([
173
+ packOpenSshString('ssh-ed25519'),
174
+ packOpenSshString(publicKey)
175
+ ]);
176
+ return [
177
+ 'ssh-ed25519',
178
+ blob.toString('base64'),
179
+ comment
180
+ ].filter(Boolean).join(' ');
181
+ };
182
+
183
+ //# sourceMappingURL=ed25519Keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/security/ed25519Keys.ts"],"sourcesContent":["import {\n createPrivateKey,\n createPublicKey,\n} from 'node:crypto'\n\nconst opensshPrivateKeyBegin = '-----BEGIN OPENSSH PRIVATE KEY-----'\nconst opensshPrivateKeyEnd = '-----END OPENSSH PRIVATE KEY-----'\nconst pkcs8Ed25519PrivateKeyDerPrefix = Buffer.from(\n '302e020100300506032b657004220420',\n 'hex',\n)\nconst spkiEd25519PublicKeyDerPrefix = Buffer.from(\n '302a300506032b6570032100',\n 'hex',\n)\n\nclass BufferReader {\n private offset = 0\n\n constructor(private readonly buffer: Buffer) {}\n\n readBytes(length: number): Buffer {\n if (length < 0 || this.remaining < length) {\n throw new DocsSyncKeyError('OpenSSH key data is truncated.')\n }\n\n const value = this.buffer.subarray(this.offset, this.offset + length)\n this.offset += length\n\n return value\n }\n\n readString(): Buffer {\n const length = this.readUInt32()\n\n return this.readBytes(length)\n }\n\n readUInt32(): number {\n if (this.remaining < 4) {\n throw new DocsSyncKeyError('OpenSSH key data is truncated.')\n }\n\n const value = this.buffer.readUInt32BE(this.offset)\n this.offset += 4\n\n return value\n }\n\n get remaining(): number {\n return this.buffer.length - this.offset\n }\n}\n\nexport class DocsSyncKeyError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'DocsSyncKeyError'\n }\n}\n\nconst packOpenSshString = (value: Buffer | string): Buffer => {\n const buffer = typeof value === 'string' ? Buffer.from(value, 'utf8') : value\n const length = Buffer.alloc(4)\n length.writeUInt32BE(buffer.length, 0)\n\n return Buffer.concat([length, buffer])\n}\n\nconst normalizeBase64 = (value: string): string => value.replace(/\\s+/g, '')\n\nconst createEd25519PrivateKeyFromSeed = (seed: Buffer) => {\n if (seed.length !== 32) {\n throw new DocsSyncKeyError('OpenSSH Ed25519 private key seed is invalid.')\n }\n\n return createPrivateKey({\n type: 'pkcs8',\n format: 'der',\n key: Buffer.concat([pkcs8Ed25519PrivateKeyDerPrefix, seed]),\n })\n}\n\nconst createEd25519PublicKeyFromRaw = (publicKey: Buffer) => {\n if (publicKey.length !== 32) {\n throw new DocsSyncKeyError('OpenSSH Ed25519 public key is invalid.')\n }\n\n return createPublicKey({\n type: 'spki',\n format: 'der',\n key: Buffer.concat([spkiEd25519PublicKeyDerPrefix, publicKey]),\n })\n}\n\nconst parseOpenSshPrivateKey = (privateKey: string) => {\n const begin = privateKey.indexOf(opensshPrivateKeyBegin)\n const end = privateKey.indexOf(opensshPrivateKeyEnd)\n\n if (begin < 0 || end < 0 || end <= begin) {\n throw new DocsSyncKeyError('OpenSSH private key PEM is invalid.')\n }\n\n const base64Body = privateKey.slice(begin + opensshPrivateKeyBegin.length, end)\n const data = Buffer.from(normalizeBase64(base64Body), 'base64')\n const authMagic = Buffer.from('openssh-key-v1\\0', 'utf8')\n\n if (!data.subarray(0, authMagic.length).equals(authMagic)) {\n throw new DocsSyncKeyError('OpenSSH private key magic header is invalid.')\n }\n\n const reader = new BufferReader(data.subarray(authMagic.length))\n const cipherName = reader.readString().toString('utf8')\n const kdfName = reader.readString().toString('utf8')\n reader.readString()\n\n if (cipherName !== 'none' || kdfName !== 'none') {\n throw new DocsSyncKeyError(\n 'Encrypted OpenSSH private keys are not supported. Use `payload-markdown-docs keygen --out .docs-sync` or provide an unencrypted PKCS#8 PEM Ed25519 private key.',\n )\n }\n\n const keyCount = reader.readUInt32()\n\n if (keyCount !== 1) {\n throw new DocsSyncKeyError('OpenSSH private key must contain exactly one key.')\n }\n\n reader.readString()\n const privateBlob = reader.readString()\n const privateReader = new BufferReader(privateBlob)\n const checkInt = privateReader.readUInt32()\n const repeatedCheckInt = privateReader.readUInt32()\n\n if (checkInt !== repeatedCheckInt) {\n throw new DocsSyncKeyError('OpenSSH private key check values do not match.')\n }\n\n const keyType = privateReader.readString().toString('utf8')\n\n if (keyType !== 'ssh-ed25519') {\n throw new DocsSyncKeyError(\n 'Only Ed25519 private keys are supported for docs sync signing.',\n )\n }\n\n const publicKey = privateReader.readString()\n const privateKeyBytes = privateReader.readString()\n\n if (privateKeyBytes.length !== 64) {\n throw new DocsSyncKeyError('OpenSSH Ed25519 private key payload is invalid.')\n }\n\n if (!privateKeyBytes.subarray(32).equals(publicKey)) {\n throw new DocsSyncKeyError('OpenSSH Ed25519 private/public key data does not match.')\n }\n\n return createEd25519PrivateKeyFromSeed(privateKeyBytes.subarray(0, 32))\n}\n\nconst parseOpenSshPublicKey = (publicKey: string) => {\n const [keyType, base64Key] = publicKey.trim().split(/\\s+/, 3)\n\n if (keyType !== 'ssh-ed25519' || !base64Key) {\n throw new DocsSyncKeyError(\n 'Only Ed25519 public keys are supported for docs sync verification.',\n )\n }\n\n const reader = new BufferReader(Buffer.from(base64Key, 'base64'))\n const parsedKeyType = reader.readString().toString('utf8')\n\n if (parsedKeyType !== 'ssh-ed25519') {\n throw new DocsSyncKeyError('OpenSSH public key type does not match ssh-ed25519.')\n }\n\n return createEd25519PublicKeyFromRaw(reader.readString())\n}\n\nexport const getEd25519PrivateKeyInput = (privateKey: string) => {\n const trimmed = privateKey.trim()\n\n if (trimmed.includes('BEGIN OPENSSH PRIVATE KEY')) {\n return parseOpenSshPrivateKey(trimmed)\n }\n\n if (trimmed.includes('BEGIN')) {\n try {\n return createPrivateKey(trimmed)\n } catch {\n throw new DocsSyncKeyError(\n 'Private key must be an Ed25519 PKCS#8 PEM key, base64 PKCS#8 DER key, or unencrypted OpenSSH Ed25519 private key.',\n )\n }\n }\n\n try {\n return createPrivateKey({\n type: 'pkcs8',\n format: 'der',\n key: Buffer.from(normalizeBase64(trimmed), 'base64'),\n })\n } catch {\n throw new DocsSyncKeyError(\n 'Private key must be an Ed25519 PKCS#8 PEM key, base64 PKCS#8 DER key, or unencrypted OpenSSH Ed25519 private key.',\n )\n }\n}\n\nexport const getEd25519PublicKeyInput = (publicKey: string) => {\n const trimmed = publicKey.trim()\n\n if (trimmed.startsWith('ssh-ed25519 ')) {\n return parseOpenSshPublicKey(trimmed)\n }\n\n if (trimmed.includes('BEGIN PUBLIC KEY')) {\n return trimmed\n }\n\n return createPublicKey({\n type: 'spki',\n format: 'der',\n key: Buffer.from(normalizeBase64(trimmed), 'base64'),\n })\n}\n\nexport const buildOpenSshEd25519PublicKey = ({\n comment,\n publicKey,\n}: {\n comment?: string\n publicKey: Buffer\n}): string => {\n const blob = Buffer.concat([\n packOpenSshString('ssh-ed25519'),\n packOpenSshString(publicKey),\n ])\n\n return ['ssh-ed25519', blob.toString('base64'), comment].filter(Boolean).join(' ')\n}\n"],"names":["createPrivateKey","createPublicKey","opensshPrivateKeyBegin","opensshPrivateKeyEnd","pkcs8Ed25519PrivateKeyDerPrefix","Buffer","from","spkiEd25519PublicKeyDerPrefix","BufferReader","offset","buffer","readBytes","length","remaining","DocsSyncKeyError","value","subarray","readString","readUInt32","readUInt32BE","Error","message","name","packOpenSshString","alloc","writeUInt32BE","concat","normalizeBase64","replace","createEd25519PrivateKeyFromSeed","seed","type","format","key","createEd25519PublicKeyFromRaw","publicKey","parseOpenSshPrivateKey","privateKey","begin","indexOf","end","base64Body","slice","data","authMagic","equals","reader","cipherName","toString","kdfName","keyCount","privateBlob","privateReader","checkInt","repeatedCheckInt","keyType","privateKeyBytes","parseOpenSshPublicKey","base64Key","trim","split","parsedKeyType","getEd25519PrivateKeyInput","trimmed","includes","getEd25519PublicKeyInput","startsWith","buildOpenSshEd25519PublicKey","comment","blob","filter","Boolean","join"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
- import type { PayloadMarkdownDocsGitHubOidcAuthConfig } from '../types.js';
2
1
  import type { FetchJson } from './jwks.js';
3
- export type GitHubOidcErrorCode = 'oidc_environment_not_allowed' | 'oidc_expired' | 'oidc_invalid_audience' | 'oidc_invalid_issuer' | 'oidc_invalid_token' | 'oidc_jwks_unavailable' | 'oidc_missing_claim' | 'oidc_missing_jti' | 'oidc_not_yet_valid' | 'oidc_owner_not_allowed' | 'oidc_pull_request_not_allowed' | 'oidc_ref_not_allowed' | 'oidc_repository_not_allowed' | 'oidc_workflow_not_allowed';
2
+ export type GitHubOidcErrorCode = 'oidc_expired' | 'oidc_invalid_audience' | 'oidc_invalid_issuer' | 'oidc_invalid_token' | 'oidc_jwks_unavailable' | 'oidc_missing_claim' | 'oidc_missing_jti' | 'oidc_not_yet_valid' | 'oidc_owner_not_allowed' | 'oidc_pull_request_not_allowed' | 'oidc_ref_not_allowed' | 'oidc_repository_not_allowed' | 'oidc_workflow_not_allowed';
4
3
  export type GitHubOidcClaims = {
5
4
  actor?: string;
6
5
  aud: string | string[];
@@ -20,6 +19,22 @@ export type GitHubOidcClaims = {
20
19
  workflow?: string;
21
20
  workflow_ref?: string;
22
21
  };
22
+ export type GitHubOidcTrustedSource = {
23
+ limitRepos?: boolean;
24
+ owner: string;
25
+ repositories?: string[];
26
+ };
27
+ export type GitHubOidcVerifyConfig = {
28
+ allowedRefs?: string[];
29
+ allowedWorkflowRefs?: string[];
30
+ allowPullRequests?: boolean;
31
+ audience: string;
32
+ enforceWorkflowRefs?: boolean;
33
+ issuer?: string;
34
+ jwksUrl?: string;
35
+ maxSkewSeconds?: number;
36
+ trustedSources: GitHubOidcTrustedSource[];
37
+ };
23
38
  export type VerifiedGitHubOidcToken = {
24
39
  claims: GitHubOidcClaims;
25
40
  expiresAt: Date;
@@ -33,11 +48,9 @@ export type VerifyGitHubOidcTokenResult = {
33
48
  ok: true;
34
49
  token: VerifiedGitHubOidcToken;
35
50
  };
36
- type GitHubOidcAuthConfig = PayloadMarkdownDocsGitHubOidcAuthConfig;
37
51
  export declare const verifyGitHubOidcToken: ({ config, fetchJson, now, token, }: {
38
- config: GitHubOidcAuthConfig;
52
+ config: GitHubOidcVerifyConfig;
39
53
  fetchJson?: FetchJson;
40
54
  now?: Date;
41
55
  token: string;
42
56
  }) => Promise<VerifyGitHubOidcTokenResult>;
43
- export {};
@@ -65,6 +65,30 @@ const includesIfConfigured = (allowed, value)=>{
65
65
  return value !== undefined && allowed.includes(value);
66
66
  };
67
67
  const audienceMatches = (audience, expected)=>Array.isArray(audience) ? audience.includes(expected) : audience === expected;
68
+ const getRepositoryName = (repository)=>{
69
+ const [, name] = repository.split('/', 2);
70
+ return name ?? repository;
71
+ };
72
+ const repositoryMatches = ({ allowed, owner, repository })=>{
73
+ const normalized = allowed.trim();
74
+ if (!normalized) {
75
+ return false;
76
+ }
77
+ return normalized.includes('/') ? normalized.toLowerCase() === repository.toLowerCase() : `${owner}/${normalized}`.toLowerCase() === repository.toLowerCase();
78
+ };
79
+ const findTrustedSource = ({ repository, repositoryOwner, trustedSources })=>trustedSources.find((source)=>{
80
+ if (source.owner.toLowerCase() !== repositoryOwner.toLowerCase()) {
81
+ return false;
82
+ }
83
+ if (source.limitRepos !== true) {
84
+ return true;
85
+ }
86
+ return (source.repositories ?? []).some((allowedRepository)=>repositoryMatches({
87
+ allowed: allowedRepository,
88
+ owner: source.owner,
89
+ repository
90
+ }));
91
+ });
68
92
  const verifyJwtSignature = ({ jwk, signature, signingInput })=>{
69
93
  try {
70
94
  const publicKey = createPublicKey({
@@ -138,28 +162,32 @@ export const verifyGitHubOidcToken = async ({ config, fetchJson, now = new Date(
138
162
  if (claims.iat - maxSkewSeconds > nowSeconds) {
139
163
  return issue('oidc_not_yet_valid', 'GitHub OIDC token was issued in the future.');
140
164
  }
141
- const hasRepositoryAllowlist = (config.allowedRepositories?.length ?? 0) > 0 || (config.allowedRepositoryOwners?.length ?? 0) > 0;
142
- if (!hasRepositoryAllowlist) {
143
- return issue('oidc_repository_not_allowed', 'GitHub OIDC auth requires an allowed repository or repository owner.');
165
+ const trustedSources = config.trustedSources ?? [];
166
+ if (trustedSources.length === 0) {
167
+ return issue('oidc_repository_not_allowed', 'GitHub OIDC auth requires a trusted GitHub owner.');
144
168
  }
145
- if (!includesIfConfigured(config.allowedRepositories, claims.repository)) {
146
- return issue('oidc_repository_not_allowed', 'GitHub OIDC token repository is not allowed.');
147
- }
148
- if (!includesIfConfigured(config.allowedRepositoryOwners, claims.repository_owner)) {
149
- return issue('oidc_owner_not_allowed', 'GitHub OIDC token repository owner is not allowed.');
169
+ const trustedSource = findTrustedSource({
170
+ repository: claims.repository,
171
+ repositoryOwner: claims.repository_owner,
172
+ trustedSources
173
+ });
174
+ if (!trustedSource) {
175
+ const matchingOwner = trustedSources.find((source)=>source.owner.toLowerCase() === claims.repository_owner.toLowerCase());
176
+ if (matchingOwner) {
177
+ return issue('oidc_repository_not_allowed', `GitHub OIDC token repository "${claims.repository}" is not trusted for owner "${claims.repository_owner}".`);
178
+ }
179
+ return issue('oidc_owner_not_allowed', `GitHub OIDC token repository owner "${claims.repository_owner}" is not trusted.`);
150
180
  }
181
+ const repositoryName = getRepositoryName(claims.repository);
151
182
  if (!includesIfConfigured(config.allowedRefs, claims.ref)) {
152
- return issue('oidc_ref_not_allowed', 'GitHub OIDC token ref is not allowed.');
153
- }
154
- if (!includesIfConfigured(config.allowedWorkflows, claims.workflow)) {
155
- return issue('oidc_workflow_not_allowed', 'GitHub OIDC token workflow is not allowed.');
183
+ return issue('oidc_ref_not_allowed', `GitHub OIDC token ref "${claims.ref}" is not allowed for "${repositoryName}".`);
156
184
  }
157
185
  const workflowRef = claims.workflow_ref ?? claims.job_workflow_ref;
158
- if (!includesIfConfigured(config.allowedWorkflowRefs, workflowRef)) {
159
- return issue('oidc_workflow_not_allowed', 'GitHub OIDC token workflow ref is not allowed.');
186
+ if (config.enforceWorkflowRefs === true && (config.allowedWorkflowRefs?.length ?? 0) === 0) {
187
+ return issue('oidc_workflow_not_allowed', 'Advanced workflow security is enabled but no workflow refs are trusted.');
160
188
  }
161
- if (!includesIfConfigured(config.allowedEnvironments, claims.environment)) {
162
- return issue('oidc_environment_not_allowed', 'GitHub OIDC token environment is not allowed.');
189
+ if (config.enforceWorkflowRefs === true && !includesIfConfigured(config.allowedWorkflowRefs, workflowRef)) {
190
+ return issue('oidc_workflow_not_allowed', 'GitHub OIDC token workflow ref is not allowed.');
163
191
  }
164
192
  if (claims.event_name === 'pull_request' && config.allowPullRequests !== true) {
165
193
  return issue('oidc_pull_request_not_allowed', 'GitHub OIDC pull request events are not allowed.');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/security/githubOidc.ts"],"sourcesContent":["import {\n createPublicKey,\n type JsonWebKey,\n verify,\n} from 'node:crypto'\n\nimport type { PayloadMarkdownDocsGitHubOidcAuthConfig } from '../types.js'\nimport type { FetchJson } from './jwks.js'\n\nimport {\n DEFAULT_GITHUB_OIDC_ISSUER,\n DEFAULT_MAX_SKEW_SECONDS,\n} from '../constants.js'\nimport {\n fetchJwks,\n findJwkByKid,\n getGithubOidcJwksUrl,\n} from './jwks.js'\nimport { decodeJwt } from './jwt.js'\n\nexport type GitHubOidcErrorCode =\n | 'oidc_environment_not_allowed'\n | 'oidc_expired'\n | 'oidc_invalid_audience'\n | 'oidc_invalid_issuer'\n | 'oidc_invalid_token'\n | 'oidc_jwks_unavailable'\n | 'oidc_missing_claim'\n | 'oidc_missing_jti'\n | 'oidc_not_yet_valid'\n | 'oidc_owner_not_allowed'\n | 'oidc_pull_request_not_allowed'\n | 'oidc_ref_not_allowed'\n | 'oidc_repository_not_allowed'\n | 'oidc_workflow_not_allowed'\n\nexport type GitHubOidcClaims = {\n actor?: string\n aud: string | string[]\n environment?: string\n event_name?: string\n exp: number\n iat: number\n iss: string\n job_workflow_ref?: string\n jti: string\n nbf?: number\n ref: string\n repository: string\n repository_owner: string\n sha?: string\n sub: string\n workflow?: string\n workflow_ref?: string\n}\n\nexport type VerifiedGitHubOidcToken = {\n claims: GitHubOidcClaims\n expiresAt: Date\n keyId: string\n}\n\nexport type VerifyGitHubOidcTokenResult =\n | {\n code: GitHubOidcErrorCode\n message: string\n ok: false\n }\n | {\n ok: true\n token: VerifiedGitHubOidcToken\n }\n\ntype GitHubOidcAuthConfig = PayloadMarkdownDocsGitHubOidcAuthConfig\n\nconst isString = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== ''\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every(isString)\n\nconst isNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value)\n\nconst getStringClaim = (\n payload: Record<string, unknown>,\n claim: string,\n): string | undefined => {\n const value = payload[claim]\n\n return isString(value) ? value : undefined\n}\n\nconst getNumberClaim = (\n payload: Record<string, unknown>,\n claim: string,\n): number | undefined => {\n const value = payload[claim]\n\n return isNumber(value) ? value : undefined\n}\n\nconst getAudienceClaim = (\n payload: Record<string, unknown>,\n): string | string[] | undefined => {\n const value = payload.aud\n\n if (isString(value) || isStringArray(value)) {\n return value\n }\n\n return undefined\n}\n\nconst toClaims = (\n payload: Record<string, unknown>,\n): GitHubOidcClaims | undefined => {\n const aud = getAudienceClaim(payload)\n const exp = getNumberClaim(payload, 'exp')\n const iat = getNumberClaim(payload, 'iat')\n const iss = getStringClaim(payload, 'iss')\n const jti = getStringClaim(payload, 'jti')\n const ref = getStringClaim(payload, 'ref')\n const repository = getStringClaim(payload, 'repository')\n const repositoryOwner = getStringClaim(payload, 'repository_owner')\n const sub = getStringClaim(payload, 'sub')\n\n if (\n !aud ||\n exp === undefined ||\n iat === undefined ||\n !iss ||\n !jti ||\n !ref ||\n !repository ||\n !repositoryOwner ||\n !sub\n ) {\n return undefined\n }\n\n return {\n actor: getStringClaim(payload, 'actor'),\n aud,\n environment: getStringClaim(payload, 'environment'),\n event_name: getStringClaim(payload, 'event_name'),\n exp,\n iat,\n iss,\n job_workflow_ref: getStringClaim(payload, 'job_workflow_ref'),\n jti,\n nbf: getNumberClaim(payload, 'nbf'),\n ref,\n repository,\n repository_owner: repositoryOwner,\n sha: getStringClaim(payload, 'sha'),\n sub,\n workflow: getStringClaim(payload, 'workflow'),\n workflow_ref: getStringClaim(payload, 'workflow_ref'),\n }\n}\n\nconst issue = (\n code: GitHubOidcErrorCode,\n message: string,\n): VerifyGitHubOidcTokenResult => ({\n code,\n message,\n ok: false,\n})\n\nconst includesIfConfigured = (\n allowed: string[] | undefined,\n value: string | undefined,\n): boolean => {\n if (!allowed || allowed.length === 0) {\n return true\n }\n\n return value !== undefined && allowed.includes(value)\n}\n\nconst audienceMatches = (\n audience: string | string[],\n expected: string,\n): boolean =>\n Array.isArray(audience) ? audience.includes(expected) : audience === expected\n\nconst verifyJwtSignature = ({\n jwk,\n signature,\n signingInput,\n}: {\n jwk: Record<string, unknown>\n signature: Buffer\n signingInput: string\n}): boolean => {\n try {\n const publicKey = createPublicKey({\n format: 'jwk',\n key: jwk as JsonWebKey,\n })\n\n return verify(\n 'RSA-SHA256',\n Buffer.from(signingInput, 'utf8'),\n publicKey,\n signature,\n )\n } catch {\n return false\n }\n}\n\nexport const verifyGitHubOidcToken = async ({\n config,\n fetchJson,\n now = new Date(),\n token,\n}: {\n config: GitHubOidcAuthConfig\n fetchJson?: FetchJson\n now?: Date\n token: string\n}): Promise<VerifyGitHubOidcTokenResult> => {\n const decoded = decodeJwt(token)\n\n if (!decoded) {\n return issue('oidc_invalid_token', 'GitHub OIDC token is malformed.')\n }\n\n if (decoded.header.alg !== 'RS256') {\n return issue('oidc_invalid_token', 'GitHub OIDC token must use RS256.')\n }\n\n if (!isString(decoded.header.kid)) {\n return issue('oidc_invalid_token', 'GitHub OIDC token is missing kid.')\n }\n\n const issuer = config.issuer ?? DEFAULT_GITHUB_OIDC_ISSUER\n let jwksUrl: string\n\n try {\n jwksUrl = await getGithubOidcJwksUrl({\n fetchJson,\n issuer,\n jwksUrl: config.jwksUrl,\n })\n const jwks = await fetchJwks({\n fetchJson,\n now,\n url: jwksUrl,\n })\n const jwk = findJwkByKid({\n jwks,\n kid: decoded.header.kid,\n })\n\n if (\n !jwk ||\n !verifyJwtSignature({\n jwk,\n signature: decoded.signature,\n signingInput: decoded.signingInput,\n })\n ) {\n return issue('oidc_invalid_token', 'GitHub OIDC token signature is invalid.')\n }\n } catch {\n return issue('oidc_jwks_unavailable', 'GitHub OIDC signing keys are unavailable.')\n }\n\n if (!isString(decoded.payload.jti)) {\n return issue('oidc_missing_jti', 'GitHub OIDC token is missing jti.')\n }\n\n const claims = toClaims(decoded.payload)\n\n if (!claims) {\n return issue('oidc_missing_claim', 'GitHub OIDC token is missing a required claim.')\n }\n\n if (claims.iss !== issuer) {\n return issue('oidc_invalid_issuer', 'GitHub OIDC token issuer is not allowed.')\n }\n\n if (!audienceMatches(claims.aud, config.audience)) {\n return issue('oidc_invalid_audience', 'GitHub OIDC token audience is not allowed.')\n }\n\n const maxSkewSeconds = config.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS\n const nowSeconds = now.getTime() / 1000\n\n if (claims.exp + maxSkewSeconds < nowSeconds) {\n return issue('oidc_expired', 'GitHub OIDC token has expired.')\n }\n\n if (claims.nbf !== undefined && claims.nbf - maxSkewSeconds > nowSeconds) {\n return issue('oidc_not_yet_valid', 'GitHub OIDC token is not valid yet.')\n }\n\n if (claims.iat - maxSkewSeconds > nowSeconds) {\n return issue('oidc_not_yet_valid', 'GitHub OIDC token was issued in the future.')\n }\n\n const hasRepositoryAllowlist =\n (config.allowedRepositories?.length ?? 0) > 0 ||\n (config.allowedRepositoryOwners?.length ?? 0) > 0\n\n if (!hasRepositoryAllowlist) {\n return issue(\n 'oidc_repository_not_allowed',\n 'GitHub OIDC auth requires an allowed repository or repository owner.',\n )\n }\n\n if (!includesIfConfigured(config.allowedRepositories, claims.repository)) {\n return issue(\n 'oidc_repository_not_allowed',\n 'GitHub OIDC token repository is not allowed.',\n )\n }\n\n if (!includesIfConfigured(config.allowedRepositoryOwners, claims.repository_owner)) {\n return issue(\n 'oidc_owner_not_allowed',\n 'GitHub OIDC token repository owner is not allowed.',\n )\n }\n\n if (!includesIfConfigured(config.allowedRefs, claims.ref)) {\n return issue('oidc_ref_not_allowed', 'GitHub OIDC token ref is not allowed.')\n }\n\n if (!includesIfConfigured(config.allowedWorkflows, claims.workflow)) {\n return issue(\n 'oidc_workflow_not_allowed',\n 'GitHub OIDC token workflow is not allowed.',\n )\n }\n\n const workflowRef = claims.workflow_ref ?? claims.job_workflow_ref\n\n if (!includesIfConfigured(config.allowedWorkflowRefs, workflowRef)) {\n return issue(\n 'oidc_workflow_not_allowed',\n 'GitHub OIDC token workflow ref is not allowed.',\n )\n }\n\n if (!includesIfConfigured(config.allowedEnvironments, claims.environment)) {\n return issue(\n 'oidc_environment_not_allowed',\n 'GitHub OIDC token environment is not allowed.',\n )\n }\n\n if (claims.event_name === 'pull_request' && config.allowPullRequests !== true) {\n return issue(\n 'oidc_pull_request_not_allowed',\n 'GitHub OIDC pull request events are not allowed.',\n )\n }\n\n return {\n ok: true,\n token: {\n claims,\n expiresAt: new Date(claims.exp * 1000),\n keyId: `github-oidc:${claims.repository}`,\n },\n }\n}\n"],"names":["createPublicKey","verify","DEFAULT_GITHUB_OIDC_ISSUER","DEFAULT_MAX_SKEW_SECONDS","fetchJwks","findJwkByKid","getGithubOidcJwksUrl","decodeJwt","isString","value","trim","isStringArray","Array","isArray","every","isNumber","Number","isFinite","getStringClaim","payload","claim","undefined","getNumberClaim","getAudienceClaim","aud","toClaims","exp","iat","iss","jti","ref","repository","repositoryOwner","sub","actor","environment","event_name","job_workflow_ref","nbf","repository_owner","sha","workflow","workflow_ref","issue","code","message","ok","includesIfConfigured","allowed","length","includes","audienceMatches","audience","expected","verifyJwtSignature","jwk","signature","signingInput","publicKey","format","key","Buffer","from","verifyGitHubOidcToken","config","fetchJson","now","Date","token","decoded","header","alg","kid","issuer","jwksUrl","jwks","url","claims","maxSkewSeconds","nowSeconds","getTime","hasRepositoryAllowlist","allowedRepositories","allowedRepositoryOwners","allowedRefs","allowedWorkflows","workflowRef","allowedWorkflowRefs","allowedEnvironments","allowPullRequests","expiresAt","keyId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/security/githubOidc.ts"],"sourcesContent":["import {\n createPublicKey,\n type JsonWebKey,\n verify,\n} from 'node:crypto'\n\nimport type { FetchJson } from './jwks.js'\n\nimport {\n DEFAULT_GITHUB_OIDC_ISSUER,\n DEFAULT_MAX_SKEW_SECONDS,\n} from '../constants.js'\nimport {\n fetchJwks,\n findJwkByKid,\n getGithubOidcJwksUrl,\n} from './jwks.js'\nimport { decodeJwt } from './jwt.js'\n\nexport type GitHubOidcErrorCode =\n | 'oidc_expired'\n | 'oidc_invalid_audience'\n | 'oidc_invalid_issuer'\n | 'oidc_invalid_token'\n | 'oidc_jwks_unavailable'\n | 'oidc_missing_claim'\n | 'oidc_missing_jti'\n | 'oidc_not_yet_valid'\n | 'oidc_owner_not_allowed'\n | 'oidc_pull_request_not_allowed'\n | 'oidc_ref_not_allowed'\n | 'oidc_repository_not_allowed'\n | 'oidc_workflow_not_allowed'\n\nexport type GitHubOidcClaims = {\n actor?: string\n aud: string | string[]\n environment?: string\n event_name?: string\n exp: number\n iat: number\n iss: string\n job_workflow_ref?: string\n jti: string\n nbf?: number\n ref: string\n repository: string\n repository_owner: string\n sha?: string\n sub: string\n workflow?: string\n workflow_ref?: string\n}\n\nexport type GitHubOidcTrustedSource = {\n limitRepos?: boolean\n owner: string\n repositories?: string[]\n}\n\nexport type GitHubOidcVerifyConfig = {\n allowedRefs?: string[]\n allowedWorkflowRefs?: string[]\n allowPullRequests?: boolean\n audience: string\n enforceWorkflowRefs?: boolean\n issuer?: string\n jwksUrl?: string\n maxSkewSeconds?: number\n trustedSources: GitHubOidcTrustedSource[]\n}\n\nexport type VerifiedGitHubOidcToken = {\n claims: GitHubOidcClaims\n expiresAt: Date\n keyId: string\n}\n\nexport type VerifyGitHubOidcTokenResult =\n | {\n code: GitHubOidcErrorCode\n message: string\n ok: false\n }\n | {\n ok: true\n token: VerifiedGitHubOidcToken\n }\n\nconst isString = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== ''\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every(isString)\n\nconst isNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value)\n\nconst getStringClaim = (\n payload: Record<string, unknown>,\n claim: string,\n): string | undefined => {\n const value = payload[claim]\n\n return isString(value) ? value : undefined\n}\n\nconst getNumberClaim = (\n payload: Record<string, unknown>,\n claim: string,\n): number | undefined => {\n const value = payload[claim]\n\n return isNumber(value) ? value : undefined\n}\n\nconst getAudienceClaim = (\n payload: Record<string, unknown>,\n): string | string[] | undefined => {\n const value = payload.aud\n\n if (isString(value) || isStringArray(value)) {\n return value\n }\n\n return undefined\n}\n\nconst toClaims = (\n payload: Record<string, unknown>,\n): GitHubOidcClaims | undefined => {\n const aud = getAudienceClaim(payload)\n const exp = getNumberClaim(payload, 'exp')\n const iat = getNumberClaim(payload, 'iat')\n const iss = getStringClaim(payload, 'iss')\n const jti = getStringClaim(payload, 'jti')\n const ref = getStringClaim(payload, 'ref')\n const repository = getStringClaim(payload, 'repository')\n const repositoryOwner = getStringClaim(payload, 'repository_owner')\n const sub = getStringClaim(payload, 'sub')\n\n if (\n !aud ||\n exp === undefined ||\n iat === undefined ||\n !iss ||\n !jti ||\n !ref ||\n !repository ||\n !repositoryOwner ||\n !sub\n ) {\n return undefined\n }\n\n return {\n actor: getStringClaim(payload, 'actor'),\n aud,\n environment: getStringClaim(payload, 'environment'),\n event_name: getStringClaim(payload, 'event_name'),\n exp,\n iat,\n iss,\n job_workflow_ref: getStringClaim(payload, 'job_workflow_ref'),\n jti,\n nbf: getNumberClaim(payload, 'nbf'),\n ref,\n repository,\n repository_owner: repositoryOwner,\n sha: getStringClaim(payload, 'sha'),\n sub,\n workflow: getStringClaim(payload, 'workflow'),\n workflow_ref: getStringClaim(payload, 'workflow_ref'),\n }\n}\n\nconst issue = (\n code: GitHubOidcErrorCode,\n message: string,\n): VerifyGitHubOidcTokenResult => ({\n code,\n message,\n ok: false,\n})\n\nconst includesIfConfigured = (\n allowed: string[] | undefined,\n value: string | undefined,\n): boolean => {\n if (!allowed || allowed.length === 0) {\n return true\n }\n\n return value !== undefined && allowed.includes(value)\n}\n\nconst audienceMatches = (\n audience: string | string[],\n expected: string,\n): boolean =>\n Array.isArray(audience) ? audience.includes(expected) : audience === expected\n\nconst getRepositoryName = (repository: string): string => {\n const [, name] = repository.split('/', 2)\n\n return name ?? repository\n}\n\nconst repositoryMatches = ({\n allowed,\n owner,\n repository,\n}: {\n allowed: string\n owner: string\n repository: string\n}): boolean => {\n const normalized = allowed.trim()\n\n if (!normalized) {\n return false\n }\n\n return normalized.includes('/')\n ? normalized.toLowerCase() === repository.toLowerCase()\n : `${owner}/${normalized}`.toLowerCase() === repository.toLowerCase()\n}\n\nconst findTrustedSource = ({\n repository,\n repositoryOwner,\n trustedSources,\n}: {\n repository: string\n repositoryOwner: string\n trustedSources: GitHubOidcTrustedSource[]\n}): GitHubOidcTrustedSource | undefined =>\n trustedSources.find((source) => {\n if (source.owner.toLowerCase() !== repositoryOwner.toLowerCase()) {\n return false\n }\n\n if (source.limitRepos !== true) {\n return true\n }\n\n return (source.repositories ?? []).some((allowedRepository) =>\n repositoryMatches({\n allowed: allowedRepository,\n owner: source.owner,\n repository,\n }),\n )\n })\n\nconst verifyJwtSignature = ({\n jwk,\n signature,\n signingInput,\n}: {\n jwk: Record<string, unknown>\n signature: Buffer\n signingInput: string\n}): boolean => {\n try {\n const publicKey = createPublicKey({\n format: 'jwk',\n key: jwk as JsonWebKey,\n })\n\n return verify(\n 'RSA-SHA256',\n Buffer.from(signingInput, 'utf8'),\n publicKey,\n signature,\n )\n } catch {\n return false\n }\n}\n\nexport const verifyGitHubOidcToken = async ({\n config,\n fetchJson,\n now = new Date(),\n token,\n}: {\n config: GitHubOidcVerifyConfig\n fetchJson?: FetchJson\n now?: Date\n token: string\n}): Promise<VerifyGitHubOidcTokenResult> => {\n const decoded = decodeJwt(token)\n\n if (!decoded) {\n return issue('oidc_invalid_token', 'GitHub OIDC token is malformed.')\n }\n\n if (decoded.header.alg !== 'RS256') {\n return issue('oidc_invalid_token', 'GitHub OIDC token must use RS256.')\n }\n\n if (!isString(decoded.header.kid)) {\n return issue('oidc_invalid_token', 'GitHub OIDC token is missing kid.')\n }\n\n const issuer = config.issuer ?? DEFAULT_GITHUB_OIDC_ISSUER\n let jwksUrl: string\n\n try {\n jwksUrl = await getGithubOidcJwksUrl({\n fetchJson,\n issuer,\n jwksUrl: config.jwksUrl,\n })\n const jwks = await fetchJwks({\n fetchJson,\n now,\n url: jwksUrl,\n })\n const jwk = findJwkByKid({\n jwks,\n kid: decoded.header.kid,\n })\n\n if (\n !jwk ||\n !verifyJwtSignature({\n jwk,\n signature: decoded.signature,\n signingInput: decoded.signingInput,\n })\n ) {\n return issue('oidc_invalid_token', 'GitHub OIDC token signature is invalid.')\n }\n } catch {\n return issue('oidc_jwks_unavailable', 'GitHub OIDC signing keys are unavailable.')\n }\n\n if (!isString(decoded.payload.jti)) {\n return issue('oidc_missing_jti', 'GitHub OIDC token is missing jti.')\n }\n\n const claims = toClaims(decoded.payload)\n\n if (!claims) {\n return issue('oidc_missing_claim', 'GitHub OIDC token is missing a required claim.')\n }\n\n if (claims.iss !== issuer) {\n return issue('oidc_invalid_issuer', 'GitHub OIDC token issuer is not allowed.')\n }\n\n if (!audienceMatches(claims.aud, config.audience)) {\n return issue('oidc_invalid_audience', 'GitHub OIDC token audience is not allowed.')\n }\n\n const maxSkewSeconds = config.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS\n const nowSeconds = now.getTime() / 1000\n\n if (claims.exp + maxSkewSeconds < nowSeconds) {\n return issue('oidc_expired', 'GitHub OIDC token has expired.')\n }\n\n if (claims.nbf !== undefined && claims.nbf - maxSkewSeconds > nowSeconds) {\n return issue('oidc_not_yet_valid', 'GitHub OIDC token is not valid yet.')\n }\n\n if (claims.iat - maxSkewSeconds > nowSeconds) {\n return issue('oidc_not_yet_valid', 'GitHub OIDC token was issued in the future.')\n }\n\n const trustedSources = config.trustedSources ?? []\n\n if (trustedSources.length === 0) {\n return issue(\n 'oidc_repository_not_allowed',\n 'GitHub OIDC auth requires a trusted GitHub owner.',\n )\n }\n\n const trustedSource = findTrustedSource({\n repository: claims.repository,\n repositoryOwner: claims.repository_owner,\n trustedSources,\n })\n\n if (!trustedSource) {\n const matchingOwner = trustedSources.find(\n (source) =>\n source.owner.toLowerCase() === claims.repository_owner.toLowerCase(),\n )\n\n if (matchingOwner) {\n return issue(\n 'oidc_repository_not_allowed',\n `GitHub OIDC token repository \"${claims.repository}\" is not trusted for owner \"${claims.repository_owner}\".`,\n )\n }\n\n return issue(\n 'oidc_owner_not_allowed',\n `GitHub OIDC token repository owner \"${claims.repository_owner}\" is not trusted.`,\n )\n }\n\n const repositoryName = getRepositoryName(claims.repository)\n\n if (!includesIfConfigured(config.allowedRefs, claims.ref)) {\n return issue(\n 'oidc_ref_not_allowed',\n `GitHub OIDC token ref \"${claims.ref}\" is not allowed for \"${repositoryName}\".`,\n )\n }\n\n const workflowRef = claims.workflow_ref ?? claims.job_workflow_ref\n\n if (\n config.enforceWorkflowRefs === true &&\n (config.allowedWorkflowRefs?.length ?? 0) === 0\n ) {\n return issue(\n 'oidc_workflow_not_allowed',\n 'Advanced workflow security is enabled but no workflow refs are trusted.',\n )\n }\n\n if (\n config.enforceWorkflowRefs === true &&\n !includesIfConfigured(config.allowedWorkflowRefs, workflowRef)\n ) {\n return issue(\n 'oidc_workflow_not_allowed',\n 'GitHub OIDC token workflow ref is not allowed.',\n )\n }\n\n if (claims.event_name === 'pull_request' && config.allowPullRequests !== true) {\n return issue(\n 'oidc_pull_request_not_allowed',\n 'GitHub OIDC pull request events are not allowed.',\n )\n }\n\n return {\n ok: true,\n token: {\n claims,\n expiresAt: new Date(claims.exp * 1000),\n keyId: `github-oidc:${claims.repository}`,\n },\n }\n}\n"],"names":["createPublicKey","verify","DEFAULT_GITHUB_OIDC_ISSUER","DEFAULT_MAX_SKEW_SECONDS","fetchJwks","findJwkByKid","getGithubOidcJwksUrl","decodeJwt","isString","value","trim","isStringArray","Array","isArray","every","isNumber","Number","isFinite","getStringClaim","payload","claim","undefined","getNumberClaim","getAudienceClaim","aud","toClaims","exp","iat","iss","jti","ref","repository","repositoryOwner","sub","actor","environment","event_name","job_workflow_ref","nbf","repository_owner","sha","workflow","workflow_ref","issue","code","message","ok","includesIfConfigured","allowed","length","includes","audienceMatches","audience","expected","getRepositoryName","name","split","repositoryMatches","owner","normalized","toLowerCase","findTrustedSource","trustedSources","find","source","limitRepos","repositories","some","allowedRepository","verifyJwtSignature","jwk","signature","signingInput","publicKey","format","key","Buffer","from","verifyGitHubOidcToken","config","fetchJson","now","Date","token","decoded","header","alg","kid","issuer","jwksUrl","jwks","url","claims","maxSkewSeconds","nowSeconds","getTime","trustedSource","matchingOwner","repositoryName","allowedRefs","workflowRef","enforceWorkflowRefs","allowedWorkflowRefs","allowPullRequests","expiresAt","keyId"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  export { buildCanonicalSigningString, getCanonicalPathFromRequestUrl, } from './canonical.js';
2
2
  export type { CanonicalSigningStringInput } from './canonical.js';
3
+ export { buildOpenSshEd25519PublicKey, DocsSyncKeyError, getEd25519PrivateKeyInput, getEd25519PublicKeyInput, } from './ed25519Keys.js';
3
4
  export { verifyGitHubOidcToken } from './githubOidc.js';
4
- export type { GitHubOidcClaims, GitHubOidcErrorCode, VerifiedGitHubOidcToken, VerifyGitHubOidcTokenResult, } from './githubOidc.js';
5
+ export type { GitHubOidcClaims, GitHubOidcErrorCode, GitHubOidcTrustedSource, GitHubOidcVerifyConfig, VerifiedGitHubOidcToken, VerifyGitHubOidcTokenResult, } from './githubOidc.js';
5
6
  export { extractSyncRequestHeaders, syncHeaderNames } from './headers.js';
6
7
  export type { ExtractSyncHeadersResult, SyncRequestHeaders, } from './headers.js';
7
8
  export type { FetchJson } from './jwks.js';
@@ -1,4 +1,5 @@
1
1
  export { buildCanonicalSigningString, getCanonicalPathFromRequestUrl } from './canonical.js';
2
+ export { buildOpenSshEd25519PublicKey, DocsSyncKeyError, getEd25519PrivateKeyInput, getEd25519PublicKeyInput } from './ed25519Keys.js';
2
3
  export { verifyGitHubOidcToken } from './githubOidc.js';
3
4
  export { extractSyncRequestHeaders, syncHeaderNames } from './headers.js';
4
5
  export { decodeJwt, toBase64Url } from './jwt.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/security/index.ts"],"sourcesContent":["export {\n buildCanonicalSigningString,\n getCanonicalPathFromRequestUrl,\n} from './canonical.js'\nexport type { CanonicalSigningStringInput } from './canonical.js'\nexport { verifyGitHubOidcToken } from './githubOidc.js'\nexport type {\n GitHubOidcClaims,\n GitHubOidcErrorCode,\n VerifiedGitHubOidcToken,\n VerifyGitHubOidcTokenResult,\n} from './githubOidc.js'\nexport { extractSyncRequestHeaders, syncHeaderNames } from './headers.js'\nexport type {\n ExtractSyncHeadersResult,\n SyncRequestHeaders,\n} from './headers.js'\nexport type { FetchJson } from './jwks.js'\nexport {\n decodeJwt,\n toBase64Url,\n} from './jwt.js'\nexport type { DecodedJwt } from './jwt.js'\nexport {\n assertNonceNotReplayed,\n storeAcceptedNonce,\n} from './nonce.js'\nexport type { NoncePayloadOperations } from './nonce.js'\nexport { signDocsSyncRequest } from './sign.js'\nexport type {\n SignDocsSyncRequestOptions,\n SignedDocsSyncRequest,\n} from './sign.js'\nexport {\n validateTimestampSkew,\n verifyBodySha256,\n verifyEd25519Signature,\n} from './verify.js'\nexport type {\n ValidateTimestampResult,\n VerifyBodyHashResult,\n} from './verify.js'\n"],"names":["buildCanonicalSigningString","getCanonicalPathFromRequestUrl","verifyGitHubOidcToken","extractSyncRequestHeaders","syncHeaderNames","decodeJwt","toBase64Url","assertNonceNotReplayed","storeAcceptedNonce","signDocsSyncRequest","validateTimestampSkew","verifyBodySha256","verifyEd25519Signature"],"mappings":"AAAA,SACEA,2BAA2B,EAC3BC,8BAA8B,QACzB,iBAAgB;AAEvB,SAASC,qBAAqB,QAAQ,kBAAiB;AAOvD,SAASC,yBAAyB,EAAEC,eAAe,QAAQ,eAAc;AAMzE,SACEC,SAAS,EACTC,WAAW,QACN,WAAU;AAEjB,SACEC,sBAAsB,EACtBC,kBAAkB,QACb,aAAY;AAEnB,SAASC,mBAAmB,QAAQ,YAAW;AAK/C,SACEC,qBAAqB,EACrBC,gBAAgB,EAChBC,sBAAsB,QACjB,cAAa"}
1
+ {"version":3,"sources":["../../src/security/index.ts"],"sourcesContent":["export {\n buildCanonicalSigningString,\n getCanonicalPathFromRequestUrl,\n} from './canonical.js'\nexport type { CanonicalSigningStringInput } from './canonical.js'\nexport {\n buildOpenSshEd25519PublicKey,\n DocsSyncKeyError,\n getEd25519PrivateKeyInput,\n getEd25519PublicKeyInput,\n} from './ed25519Keys.js'\nexport { verifyGitHubOidcToken } from './githubOidc.js'\nexport type {\n GitHubOidcClaims,\n GitHubOidcErrorCode,\n GitHubOidcTrustedSource,\n GitHubOidcVerifyConfig,\n VerifiedGitHubOidcToken,\n VerifyGitHubOidcTokenResult,\n} from './githubOidc.js'\nexport { extractSyncRequestHeaders, syncHeaderNames } from './headers.js'\nexport type {\n ExtractSyncHeadersResult,\n SyncRequestHeaders,\n} from './headers.js'\nexport type { FetchJson } from './jwks.js'\nexport {\n decodeJwt,\n toBase64Url,\n} from './jwt.js'\nexport type { DecodedJwt } from './jwt.js'\nexport {\n assertNonceNotReplayed,\n storeAcceptedNonce,\n} from './nonce.js'\nexport type { NoncePayloadOperations } from './nonce.js'\nexport { signDocsSyncRequest } from './sign.js'\nexport type {\n SignDocsSyncRequestOptions,\n SignedDocsSyncRequest,\n} from './sign.js'\nexport {\n validateTimestampSkew,\n verifyBodySha256,\n verifyEd25519Signature,\n} from './verify.js'\nexport type {\n ValidateTimestampResult,\n VerifyBodyHashResult,\n} from './verify.js'\n"],"names":["buildCanonicalSigningString","getCanonicalPathFromRequestUrl","buildOpenSshEd25519PublicKey","DocsSyncKeyError","getEd25519PrivateKeyInput","getEd25519PublicKeyInput","verifyGitHubOidcToken","extractSyncRequestHeaders","syncHeaderNames","decodeJwt","toBase64Url","assertNonceNotReplayed","storeAcceptedNonce","signDocsSyncRequest","validateTimestampSkew","verifyBodySha256","verifyEd25519Signature"],"mappings":"AAAA,SACEA,2BAA2B,EAC3BC,8BAA8B,QACzB,iBAAgB;AAEvB,SACEC,4BAA4B,EAC5BC,gBAAgB,EAChBC,yBAAyB,EACzBC,wBAAwB,QACnB,mBAAkB;AACzB,SAASC,qBAAqB,QAAQ,kBAAiB;AASvD,SAASC,yBAAyB,EAAEC,eAAe,QAAQ,eAAc;AAMzE,SACEC,SAAS,EACTC,WAAW,QACN,WAAU;AAEjB,SACEC,sBAAsB,EACtBC,kBAAkB,QACb,aAAY;AAEnB,SAASC,mBAAmB,QAAQ,YAAW;AAK/C,SACEC,qBAAqB,EACrBC,gBAAgB,EAChBC,sBAAsB,QACjB,cAAa"}
@@ -1,16 +1,7 @@
1
- import { createPrivateKey, randomUUID, sign } from 'node:crypto';
1
+ import { randomUUID, sign } from 'node:crypto';
2
2
  import { sha256Hex } from '../sync/index.js';
3
3
  import { buildCanonicalSigningString } from './canonical.js';
4
- const getPrivateKeyInput = (privateKey)=>{
5
- if (privateKey.includes('BEGIN PRIVATE KEY')) {
6
- return privateKey;
7
- }
8
- return createPrivateKey({
9
- type: 'pkcs8',
10
- format: 'der',
11
- key: Buffer.from(privateKey, 'base64')
12
- });
13
- };
4
+ import { getEd25519PrivateKeyInput } from './ed25519Keys.js';
14
5
  const getEndpointPathname = (endpoint)=>new URL(endpoint).pathname;
15
6
  export const signDocsSyncRequest = ({ body, endpoint, keyId, nonce = randomUUID(), now = new Date(), privateKey })=>{
16
7
  const bodySha256 = sha256Hex(body);
@@ -22,7 +13,7 @@ export const signDocsSyncRequest = ({ body, endpoint, keyId, nonce = randomUUID(
22
13
  path: getEndpointPathname(endpoint),
23
14
  timestamp
24
15
  });
25
- const signature = sign(null, Buffer.from(canonicalString, 'utf8'), getPrivateKeyInput(privateKey)).toString('base64');
16
+ const signature = sign(null, Buffer.from(canonicalString, 'utf8'), getEd25519PrivateKeyInput(privateKey)).toString('base64');
26
17
  return {
27
18
  body,
28
19
  headers: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/security/sign.ts"],"sourcesContent":["import {\n createPrivateKey,\n randomUUID,\n sign,\n} from 'node:crypto'\n\nimport { sha256Hex } from '../sync/index.js'\nimport { buildCanonicalSigningString } from './canonical.js'\n\nexport type SignDocsSyncRequestOptions = {\n body: string\n endpoint: string\n keyId: string\n nonce?: string\n now?: Date\n privateKey: string\n}\n\nexport type SignedDocsSyncRequest = {\n body: string\n headers: Record<string, string>\n}\n\nconst getPrivateKeyInput = (privateKey: string) => {\n if (privateKey.includes('BEGIN PRIVATE KEY')) {\n return privateKey\n }\n\n return createPrivateKey({\n type: 'pkcs8',\n format: 'der',\n key: Buffer.from(privateKey, 'base64'),\n })\n}\n\nconst getEndpointPathname = (endpoint: string): string => new URL(endpoint).pathname\n\nexport const signDocsSyncRequest = ({\n body,\n endpoint,\n keyId,\n nonce = randomUUID(),\n now = new Date(),\n privateKey,\n}: SignDocsSyncRequestOptions): SignedDocsSyncRequest => {\n const bodySha256 = sha256Hex(body)\n const timestamp = now.toISOString()\n const canonicalString = buildCanonicalSigningString({\n bodySha256,\n method: 'POST',\n nonce,\n path: getEndpointPathname(endpoint),\n timestamp,\n })\n const signature = sign(\n null,\n Buffer.from(canonicalString, 'utf8'),\n getPrivateKeyInput(privateKey),\n ).toString('base64')\n\n return {\n body,\n headers: {\n 'Content-Type': 'application/json',\n 'X-VL-MD-DOCS-Body-SHA256': bodySha256,\n 'X-VL-MD-DOCS-Key-Id': keyId,\n 'X-VL-MD-DOCS-Nonce': nonce,\n 'X-VL-MD-DOCS-Signature': signature,\n 'X-VL-MD-DOCS-Timestamp': timestamp,\n },\n }\n}\n"],"names":["createPrivateKey","randomUUID","sign","sha256Hex","buildCanonicalSigningString","getPrivateKeyInput","privateKey","includes","type","format","key","Buffer","from","getEndpointPathname","endpoint","URL","pathname","signDocsSyncRequest","body","keyId","nonce","now","Date","bodySha256","timestamp","toISOString","canonicalString","method","path","signature","toString","headers"],"mappings":"AAAA,SACEA,gBAAgB,EAChBC,UAAU,EACVC,IAAI,QACC,cAAa;AAEpB,SAASC,SAAS,QAAQ,mBAAkB;AAC5C,SAASC,2BAA2B,QAAQ,iBAAgB;AAgB5D,MAAMC,qBAAqB,CAACC;IAC1B,IAAIA,WAAWC,QAAQ,CAAC,sBAAsB;QAC5C,OAAOD;IACT;IAEA,OAAON,iBAAiB;QACtBQ,MAAM;QACNC,QAAQ;QACRC,KAAKC,OAAOC,IAAI,CAACN,YAAY;IAC/B;AACF;AAEA,MAAMO,sBAAsB,CAACC,WAA6B,IAAIC,IAAID,UAAUE,QAAQ;AAEpF,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,IAAI,EACJJ,QAAQ,EACRK,KAAK,EACLC,QAAQnB,YAAY,EACpBoB,MAAM,IAAIC,MAAM,EAChBhB,UAAU,EACiB;IAC3B,MAAMiB,aAAapB,UAAUe;IAC7B,MAAMM,YAAYH,IAAII,WAAW;IACjC,MAAMC,kBAAkBtB,4BAA4B;QAClDmB;QACAI,QAAQ;QACRP;QACAQ,MAAMf,oBAAoBC;QAC1BU;IACF;IACA,MAAMK,YAAY3B,KAChB,MACAS,OAAOC,IAAI,CAACc,iBAAiB,SAC7BrB,mBAAmBC,aACnBwB,QAAQ,CAAC;IAEX,OAAO;QACLZ;QACAa,SAAS;YACP,gBAAgB;YAChB,4BAA4BR;YAC5B,uBAAuBJ;YACvB,sBAAsBC;YACtB,0BAA0BS;YAC1B,0BAA0BL;QAC5B;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/security/sign.ts"],"sourcesContent":["import {\n randomUUID,\n sign,\n} from 'node:crypto'\n\nimport { sha256Hex } from '../sync/index.js'\nimport { buildCanonicalSigningString } from './canonical.js'\nimport { getEd25519PrivateKeyInput } from './ed25519Keys.js'\n\nexport type SignDocsSyncRequestOptions = {\n body: string\n endpoint: string\n keyId: string\n nonce?: string\n now?: Date\n privateKey: string\n}\n\nexport type SignedDocsSyncRequest = {\n body: string\n headers: Record<string, string>\n}\n\nconst getEndpointPathname = (endpoint: string): string => new URL(endpoint).pathname\n\nexport const signDocsSyncRequest = ({\n body,\n endpoint,\n keyId,\n nonce = randomUUID(),\n now = new Date(),\n privateKey,\n}: SignDocsSyncRequestOptions): SignedDocsSyncRequest => {\n const bodySha256 = sha256Hex(body)\n const timestamp = now.toISOString()\n const canonicalString = buildCanonicalSigningString({\n bodySha256,\n method: 'POST',\n nonce,\n path: getEndpointPathname(endpoint),\n timestamp,\n })\n const signature = sign(\n null,\n Buffer.from(canonicalString, 'utf8'),\n getEd25519PrivateKeyInput(privateKey),\n ).toString('base64')\n\n return {\n body,\n headers: {\n 'Content-Type': 'application/json',\n 'X-VL-MD-DOCS-Body-SHA256': bodySha256,\n 'X-VL-MD-DOCS-Key-Id': keyId,\n 'X-VL-MD-DOCS-Nonce': nonce,\n 'X-VL-MD-DOCS-Signature': signature,\n 'X-VL-MD-DOCS-Timestamp': timestamp,\n },\n }\n}\n"],"names":["randomUUID","sign","sha256Hex","buildCanonicalSigningString","getEd25519PrivateKeyInput","getEndpointPathname","endpoint","URL","pathname","signDocsSyncRequest","body","keyId","nonce","now","Date","privateKey","bodySha256","timestamp","toISOString","canonicalString","method","path","signature","Buffer","from","toString","headers"],"mappings":"AAAA,SACEA,UAAU,EACVC,IAAI,QACC,cAAa;AAEpB,SAASC,SAAS,QAAQ,mBAAkB;AAC5C,SAASC,2BAA2B,QAAQ,iBAAgB;AAC5D,SAASC,yBAAyB,QAAQ,mBAAkB;AAgB5D,MAAMC,sBAAsB,CAACC,WAA6B,IAAIC,IAAID,UAAUE,QAAQ;AAEpF,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,IAAI,EACJJ,QAAQ,EACRK,KAAK,EACLC,QAAQZ,YAAY,EACpBa,MAAM,IAAIC,MAAM,EAChBC,UAAU,EACiB;IAC3B,MAAMC,aAAad,UAAUQ;IAC7B,MAAMO,YAAYJ,IAAIK,WAAW;IACjC,MAAMC,kBAAkBhB,4BAA4B;QAClDa;QACAI,QAAQ;QACRR;QACAS,MAAMhB,oBAAoBC;QAC1BW;IACF;IACA,MAAMK,YAAYrB,KAChB,MACAsB,OAAOC,IAAI,CAACL,iBAAiB,SAC7Bf,0BAA0BW,aAC1BU,QAAQ,CAAC;IAEX,OAAO;QACLf;QACAgB,SAAS;YACP,gBAAgB;YAChB,4BAA4BV;YAC5B,uBAAuBL;YACvB,sBAAsBC;YACtB,0BAA0BU;YAC1B,0BAA0BL;QAC5B;IACF;AACF,EAAC"}