@pan-sec/notebooklm-mcp 2026.3.3 → 2026.4.0
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.
- package/dist/auth/auth-manager.d.ts +0 -1
- package/dist/auth/auth-manager.js +0 -1
- package/dist/auth/mcp-auth.d.ts +0 -1
- package/dist/auth/mcp-auth.js +0 -1
- package/dist/compliance/alert-manager.d.ts +6 -2
- package/dist/compliance/alert-manager.js +40 -10
- package/dist/compliance/breach-detection.d.ts +0 -1
- package/dist/compliance/breach-detection.js +0 -1
- package/dist/compliance/change-log.d.ts +13 -1
- package/dist/compliance/change-log.js +82 -16
- package/dist/compliance/compliance-logger.d.ts +29 -3
- package/dist/compliance/compliance-logger.js +90 -27
- package/dist/compliance/compliance-tools.d.ts +0 -1
- package/dist/compliance/compliance-tools.js +0 -1
- package/dist/compliance/consent-manager.d.ts +0 -1
- package/dist/compliance/consent-manager.js +0 -1
- package/dist/compliance/dashboard.d.ts +4 -3
- package/dist/compliance/dashboard.js +11 -8
- package/dist/compliance/data-classification.d.ts +0 -1
- package/dist/compliance/data-classification.js +0 -1
- package/dist/compliance/data-erasure.d.ts +0 -1
- package/dist/compliance/data-erasure.js +0 -1
- package/dist/compliance/data-export.d.ts +0 -1
- package/dist/compliance/data-export.js +0 -1
- package/dist/compliance/data-inventory.d.ts +0 -1
- package/dist/compliance/data-inventory.js +0 -1
- package/dist/compliance/dsar-handler.d.ts +0 -1
- package/dist/compliance/dsar-handler.js +0 -1
- package/dist/compliance/evidence-collector.d.ts +0 -1
- package/dist/compliance/evidence-collector.js +4 -2
- package/dist/compliance/health-monitor.d.ts +0 -1
- package/dist/compliance/health-monitor.js +0 -1
- package/dist/compliance/incident-manager.d.ts +0 -1
- package/dist/compliance/incident-manager.js +0 -1
- package/dist/compliance/index.d.ts +0 -1
- package/dist/compliance/index.js +0 -1
- package/dist/compliance/policy-docs.d.ts +0 -1
- package/dist/compliance/policy-docs.js +0 -1
- package/dist/compliance/privacy-notice-text.d.ts +0 -1
- package/dist/compliance/privacy-notice-text.js +0 -1
- package/dist/compliance/privacy-notice.d.ts +0 -1
- package/dist/compliance/privacy-notice.js +0 -1
- package/dist/compliance/report-generator.d.ts +7 -1
- package/dist/compliance/report-generator.js +116 -34
- package/dist/compliance/retention-engine.d.ts +0 -1
- package/dist/compliance/retention-engine.js +0 -1
- package/dist/compliance/siem-exporter.d.ts +26 -2
- package/dist/compliance/siem-exporter.js +89 -24
- package/dist/compliance/types.d.ts +0 -1
- package/dist/compliance/types.js +0 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.js +2 -3
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js +0 -1
- package/dist/events/event-emitter.d.ts +9 -1
- package/dist/events/event-emitter.js +47 -8
- package/dist/events/event-types.d.ts +0 -1
- package/dist/events/event-types.js +8 -2
- package/dist/gemini/gemini-client.d.ts +0 -1
- package/dist/gemini/gemini-client.js +237 -45
- package/dist/gemini/index.d.ts +0 -1
- package/dist/gemini/index.js +0 -1
- package/dist/gemini/pdf-chunker.d.ts +0 -1
- package/dist/gemini/pdf-chunker.js +60 -35
- package/dist/gemini/types.d.ts +0 -1
- package/dist/gemini/types.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +60 -7
- package/dist/library/notebook-library.d.ts +30 -2
- package/dist/library/notebook-library.js +345 -85
- package/dist/library/types.d.ts +0 -1
- package/dist/library/types.js +0 -1
- package/dist/logging/index.d.ts +0 -1
- package/dist/logging/index.js +0 -1
- package/dist/logging/query-logger.d.ts +20 -1
- package/dist/logging/query-logger.js +104 -21
- package/dist/notebook-creation/audio-manager.d.ts +0 -1
- package/dist/notebook-creation/audio-manager.js +111 -20
- package/dist/notebook-creation/browser-options.d.ts +0 -1
- package/dist/notebook-creation/browser-options.js +0 -1
- package/dist/notebook-creation/data-table-manager.d.ts +7 -1
- package/dist/notebook-creation/data-table-manager.js +59 -3
- package/dist/notebook-creation/dom-scripts.d.ts +0 -1
- package/dist/notebook-creation/dom-scripts.js +0 -1
- package/dist/notebook-creation/errors.d.ts +0 -1
- package/dist/notebook-creation/errors.js +0 -1
- package/dist/notebook-creation/index.d.ts +0 -1
- package/dist/notebook-creation/index.js +0 -1
- package/dist/notebook-creation/notebook-creator.d.ts +9 -1
- package/dist/notebook-creation/notebook-creator.js +50 -1
- package/dist/notebook-creation/notebook-nav.d.ts +0 -1
- package/dist/notebook-creation/notebook-nav.js +21 -6
- package/dist/notebook-creation/notebook-sync.d.ts +14 -2
- package/dist/notebook-creation/notebook-sync.js +124 -35
- package/dist/notebook-creation/selectors.d.ts +0 -1
- package/dist/notebook-creation/selectors.js +6 -4
- package/dist/notebook-creation/source-manager.d.ts +29 -2
- package/dist/notebook-creation/source-manager.js +0 -0
- package/dist/notebook-creation/types.d.ts +0 -1
- package/dist/notebook-creation/types.js +0 -1
- package/dist/notebook-creation/video-manager.d.ts +0 -1
- package/dist/notebook-creation/video-manager.js +91 -15
- package/dist/observability/metrics.d.ts +0 -1
- package/dist/observability/metrics.js +0 -1
- package/dist/quota/index.d.ts +0 -1
- package/dist/quota/index.js +0 -1
- package/dist/quota/quota-manager.d.ts +59 -4
- package/dist/quota/quota-manager.js +195 -46
- package/dist/resources/resource-handlers.d.ts +0 -1
- package/dist/resources/resource-handlers.js +33 -3
- package/dist/session/browser-session.d.ts +0 -1
- package/dist/session/browser-session.js +0 -1
- package/dist/session/session-manager.d.ts +0 -1
- package/dist/session/session-manager.js +0 -1
- package/dist/session/session-timeout.d.ts +0 -1
- package/dist/session/session-timeout.js +0 -1
- package/dist/session/shared-context-manager.d.ts +0 -1
- package/dist/session/shared-context-manager.js +0 -1
- package/dist/tools/annotations.d.ts +0 -1
- package/dist/tools/annotations.js +0 -1
- package/dist/tools/definitions/ask-question.d.ts +6 -3
- package/dist/tools/definitions/ask-question.js +12 -8
- package/dist/tools/definitions/chat-history.d.ts +0 -1
- package/dist/tools/definitions/chat-history.js +1 -1
- package/dist/tools/definitions/data-tables.d.ts +0 -1
- package/dist/tools/definitions/data-tables.js +4 -1
- package/dist/tools/definitions/gemini.d.ts +0 -1
- package/dist/tools/definitions/gemini.js +14 -7
- package/dist/tools/definitions/notebook-management.d.ts +0 -1
- package/dist/tools/definitions/notebook-management.js +7 -2
- package/dist/tools/definitions/query-history.d.ts +0 -1
- package/dist/tools/definitions/query-history.js +0 -1
- package/dist/tools/definitions/session-management.d.ts +0 -1
- package/dist/tools/definitions/session-management.js +0 -1
- package/dist/tools/definitions/system.d.ts +0 -1
- package/dist/tools/definitions/system.js +32 -12
- package/dist/tools/definitions/video.d.ts +0 -1
- package/dist/tools/definitions/video.js +6 -3
- package/dist/tools/definitions.d.ts +0 -1
- package/dist/tools/definitions.js +0 -1
- package/dist/tools/handlers/ask-question.d.ts +0 -1
- package/dist/tools/handlers/ask-question.js +47 -18
- package/dist/tools/handlers/audio-video.d.ts +0 -1
- package/dist/tools/handlers/audio-video.js +0 -1
- package/dist/tools/handlers/auth.d.ts +0 -1
- package/dist/tools/handlers/auth.js +0 -1
- package/dist/tools/handlers/error-utils.d.ts +0 -1
- package/dist/tools/handlers/error-utils.js +0 -1
- package/dist/tools/handlers/gemini.d.ts +0 -1
- package/dist/tools/handlers/gemini.js +0 -1
- package/dist/tools/handlers/index.d.ts +0 -1
- package/dist/tools/handlers/index.js +0 -1
- package/dist/tools/handlers/notebook-creation.d.ts +0 -1
- package/dist/tools/handlers/notebook-creation.js +16 -1
- package/dist/tools/handlers/notebook-management.d.ts +0 -1
- package/dist/tools/handlers/notebook-management.js +7 -2
- package/dist/tools/handlers/session-management.d.ts +0 -1
- package/dist/tools/handlers/session-management.js +0 -1
- package/dist/tools/handlers/system.d.ts +0 -1
- package/dist/tools/handlers/system.js +0 -1
- package/dist/tools/handlers/types.d.ts +0 -1
- package/dist/tools/handlers/types.js +0 -1
- package/dist/tools/handlers/webhooks.d.ts +0 -1
- package/dist/tools/handlers/webhooks.js +0 -1
- package/dist/tools/icons.d.ts +0 -1
- package/dist/tools/icons.js +0 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/dist/utils/audit-logger.d.ts +11 -1
- package/dist/utils/audit-logger.js +189 -21
- package/dist/utils/cleanup-manager.d.ts +0 -1
- package/dist/utils/cleanup-manager.js +0 -1
- package/dist/utils/cli-handler.d.ts +0 -1
- package/dist/utils/cli-handler.js +0 -1
- package/dist/utils/crypto.d.ts +18 -9
- package/dist/utils/crypto.js +93 -28
- package/dist/utils/file-lock.d.ts +15 -1
- package/dist/utils/file-lock.js +67 -59
- package/dist/utils/file-permissions.d.ts +0 -1
- package/dist/utils/file-permissions.js +35 -7
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -1
- package/dist/utils/page-utils.d.ts +0 -1
- package/dist/utils/page-utils.js +32 -28
- package/dist/utils/response-validator.d.ts +0 -1
- package/dist/utils/response-validator.js +18 -15
- package/dist/utils/secrets-scanner.d.ts +0 -1
- package/dist/utils/secrets-scanner.js +32 -7
- package/dist/utils/secure-memory.d.ts +34 -16
- package/dist/utils/secure-memory.js +40 -25
- package/dist/utils/security.d.ts +0 -1
- package/dist/utils/security.js +66 -39
- package/dist/utils/settings-manager.d.ts +9 -1
- package/dist/utils/settings-manager.js +45 -2
- package/dist/utils/stealth-utils.d.ts +0 -1
- package/dist/utils/stealth-utils.js +11 -9
- package/dist/webhooks/index.d.ts +0 -1
- package/dist/webhooks/index.js +0 -1
- package/dist/webhooks/types.d.ts +0 -1
- package/dist/webhooks/types.js +0 -1
- package/dist/webhooks/webhook-dispatcher.d.ts +0 -1
- package/dist/webhooks/webhook-dispatcher.js +0 -1
- package/package.json +5 -4
- package/dist/auth/auth-manager.d.ts.map +0 -1
- package/dist/auth/auth-manager.js.map +0 -1
- package/dist/auth/mcp-auth.d.ts.map +0 -1
- package/dist/auth/mcp-auth.js.map +0 -1
- package/dist/compliance/alert-manager.d.ts.map +0 -1
- package/dist/compliance/alert-manager.js.map +0 -1
- package/dist/compliance/breach-detection.d.ts.map +0 -1
- package/dist/compliance/breach-detection.js.map +0 -1
- package/dist/compliance/change-log.d.ts.map +0 -1
- package/dist/compliance/change-log.js.map +0 -1
- package/dist/compliance/compliance-logger.d.ts.map +0 -1
- package/dist/compliance/compliance-logger.js.map +0 -1
- package/dist/compliance/compliance-tools.d.ts.map +0 -1
- package/dist/compliance/compliance-tools.js.map +0 -1
- package/dist/compliance/consent-manager.d.ts.map +0 -1
- package/dist/compliance/consent-manager.js.map +0 -1
- package/dist/compliance/dashboard.d.ts.map +0 -1
- package/dist/compliance/dashboard.js.map +0 -1
- package/dist/compliance/data-classification.d.ts.map +0 -1
- package/dist/compliance/data-classification.js.map +0 -1
- package/dist/compliance/data-erasure.d.ts.map +0 -1
- package/dist/compliance/data-erasure.js.map +0 -1
- package/dist/compliance/data-export.d.ts.map +0 -1
- package/dist/compliance/data-export.js.map +0 -1
- package/dist/compliance/data-inventory.d.ts.map +0 -1
- package/dist/compliance/data-inventory.js.map +0 -1
- package/dist/compliance/dsar-handler.d.ts.map +0 -1
- package/dist/compliance/dsar-handler.js.map +0 -1
- package/dist/compliance/evidence-collector.d.ts.map +0 -1
- package/dist/compliance/evidence-collector.js.map +0 -1
- package/dist/compliance/health-monitor.d.ts.map +0 -1
- package/dist/compliance/health-monitor.js.map +0 -1
- package/dist/compliance/incident-manager.d.ts.map +0 -1
- package/dist/compliance/incident-manager.js.map +0 -1
- package/dist/compliance/index.d.ts.map +0 -1
- package/dist/compliance/index.js.map +0 -1
- package/dist/compliance/policy-docs.d.ts.map +0 -1
- package/dist/compliance/policy-docs.js.map +0 -1
- package/dist/compliance/privacy-notice-text.d.ts.map +0 -1
- package/dist/compliance/privacy-notice-text.js.map +0 -1
- package/dist/compliance/privacy-notice.d.ts.map +0 -1
- package/dist/compliance/privacy-notice.js.map +0 -1
- package/dist/compliance/report-generator.d.ts.map +0 -1
- package/dist/compliance/report-generator.js.map +0 -1
- package/dist/compliance/retention-engine.d.ts.map +0 -1
- package/dist/compliance/retention-engine.js.map +0 -1
- package/dist/compliance/siem-exporter.d.ts.map +0 -1
- package/dist/compliance/siem-exporter.js.map +0 -1
- package/dist/compliance/types.d.ts.map +0 -1
- package/dist/compliance/types.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/events/event-emitter.d.ts.map +0 -1
- package/dist/events/event-emitter.js.map +0 -1
- package/dist/events/event-types.d.ts.map +0 -1
- package/dist/events/event-types.js.map +0 -1
- package/dist/gemini/gemini-client.d.ts.map +0 -1
- package/dist/gemini/gemini-client.js.map +0 -1
- package/dist/gemini/index.d.ts.map +0 -1
- package/dist/gemini/index.js.map +0 -1
- package/dist/gemini/pdf-chunker.d.ts.map +0 -1
- package/dist/gemini/pdf-chunker.js.map +0 -1
- package/dist/gemini/types.d.ts.map +0 -1
- package/dist/gemini/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/library/notebook-library.d.ts.map +0 -1
- package/dist/library/notebook-library.js.map +0 -1
- package/dist/library/types.d.ts.map +0 -1
- package/dist/library/types.js.map +0 -1
- package/dist/logging/index.d.ts.map +0 -1
- package/dist/logging/index.js.map +0 -1
- package/dist/logging/query-logger.d.ts.map +0 -1
- package/dist/logging/query-logger.js.map +0 -1
- package/dist/notebook-creation/audio-manager.d.ts.map +0 -1
- package/dist/notebook-creation/audio-manager.js.map +0 -1
- package/dist/notebook-creation/browser-options.d.ts.map +0 -1
- package/dist/notebook-creation/browser-options.js.map +0 -1
- package/dist/notebook-creation/data-table-manager.d.ts.map +0 -1
- package/dist/notebook-creation/data-table-manager.js.map +0 -1
- package/dist/notebook-creation/discover-creation-flow.d.ts +0 -2
- package/dist/notebook-creation/discover-creation-flow.d.ts.map +0 -1
- package/dist/notebook-creation/discover-creation-flow.js +0 -177
- package/dist/notebook-creation/discover-creation-flow.js.map +0 -1
- package/dist/notebook-creation/discover-quota.d.ts +0 -2
- package/dist/notebook-creation/discover-quota.d.ts.map +0 -1
- package/dist/notebook-creation/discover-quota.js +0 -194
- package/dist/notebook-creation/discover-quota.js.map +0 -1
- package/dist/notebook-creation/discover-source-dialog.d.ts +0 -8
- package/dist/notebook-creation/discover-source-dialog.d.ts.map +0 -1
- package/dist/notebook-creation/discover-source-dialog.js +0 -134
- package/dist/notebook-creation/discover-source-dialog.js.map +0 -1
- package/dist/notebook-creation/discover-sources.d.ts +0 -8
- package/dist/notebook-creation/discover-sources.d.ts.map +0 -1
- package/dist/notebook-creation/discover-sources.js +0 -272
- package/dist/notebook-creation/discover-sources.js.map +0 -1
- package/dist/notebook-creation/discover-text-input.d.ts +0 -7
- package/dist/notebook-creation/discover-text-input.d.ts.map +0 -1
- package/dist/notebook-creation/discover-text-input.js +0 -135
- package/dist/notebook-creation/discover-text-input.js.map +0 -1
- package/dist/notebook-creation/dom-scripts.d.ts.map +0 -1
- package/dist/notebook-creation/dom-scripts.js.map +0 -1
- package/dist/notebook-creation/errors.d.ts.map +0 -1
- package/dist/notebook-creation/errors.js.map +0 -1
- package/dist/notebook-creation/index.d.ts.map +0 -1
- package/dist/notebook-creation/index.js.map +0 -1
- package/dist/notebook-creation/notebook-creator.d.ts.map +0 -1
- package/dist/notebook-creation/notebook-creator.js.map +0 -1
- package/dist/notebook-creation/notebook-nav.d.ts.map +0 -1
- package/dist/notebook-creation/notebook-nav.js.map +0 -1
- package/dist/notebook-creation/notebook-sync.d.ts.map +0 -1
- package/dist/notebook-creation/notebook-sync.js.map +0 -1
- package/dist/notebook-creation/run-discovery.d.ts +0 -11
- package/dist/notebook-creation/run-discovery.d.ts.map +0 -1
- package/dist/notebook-creation/run-discovery.js +0 -151
- package/dist/notebook-creation/run-discovery.js.map +0 -1
- package/dist/notebook-creation/selector-discovery.d.ts +0 -65
- package/dist/notebook-creation/selector-discovery.d.ts.map +0 -1
- package/dist/notebook-creation/selector-discovery.js +0 -414
- package/dist/notebook-creation/selector-discovery.js.map +0 -1
- package/dist/notebook-creation/selectors.d.ts.map +0 -1
- package/dist/notebook-creation/selectors.js.map +0 -1
- package/dist/notebook-creation/selectors.ts +0 -112
- package/dist/notebook-creation/source-manager.d.ts.map +0 -1
- package/dist/notebook-creation/source-manager.js.map +0 -1
- package/dist/notebook-creation/test-create.d.ts +0 -8
- package/dist/notebook-creation/test-create.d.ts.map +0 -1
- package/dist/notebook-creation/test-create.js +0 -72
- package/dist/notebook-creation/test-create.js.map +0 -1
- package/dist/notebook-creation/types.d.ts.map +0 -1
- package/dist/notebook-creation/types.js.map +0 -1
- package/dist/notebook-creation/video-manager.d.ts.map +0 -1
- package/dist/notebook-creation/video-manager.js.map +0 -1
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js.map +0 -1
- package/dist/quota/index.d.ts.map +0 -1
- package/dist/quota/index.js.map +0 -1
- package/dist/quota/quota-manager.d.ts.map +0 -1
- package/dist/quota/quota-manager.js.map +0 -1
- package/dist/resources/resource-handlers.d.ts.map +0 -1
- package/dist/resources/resource-handlers.js.map +0 -1
- package/dist/session/browser-session.d.ts.map +0 -1
- package/dist/session/browser-session.js.map +0 -1
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/session/session-manager.js.map +0 -1
- package/dist/session/session-timeout.d.ts.map +0 -1
- package/dist/session/session-timeout.js.map +0 -1
- package/dist/session/shared-context-manager.d.ts.map +0 -1
- package/dist/session/shared-context-manager.js.map +0 -1
- package/dist/tools/annotations.d.ts.map +0 -1
- package/dist/tools/annotations.js.map +0 -1
- package/dist/tools/definitions/ask-question.d.ts.map +0 -1
- package/dist/tools/definitions/ask-question.js.map +0 -1
- package/dist/tools/definitions/chat-history.d.ts.map +0 -1
- package/dist/tools/definitions/chat-history.js.map +0 -1
- package/dist/tools/definitions/data-tables.d.ts.map +0 -1
- package/dist/tools/definitions/data-tables.js.map +0 -1
- package/dist/tools/definitions/gemini.d.ts.map +0 -1
- package/dist/tools/definitions/gemini.js.map +0 -1
- package/dist/tools/definitions/notebook-management.d.ts.map +0 -1
- package/dist/tools/definitions/notebook-management.js.map +0 -1
- package/dist/tools/definitions/query-history.d.ts.map +0 -1
- package/dist/tools/definitions/query-history.js.map +0 -1
- package/dist/tools/definitions/session-management.d.ts.map +0 -1
- package/dist/tools/definitions/session-management.js.map +0 -1
- package/dist/tools/definitions/system.d.ts.map +0 -1
- package/dist/tools/definitions/system.js.map +0 -1
- package/dist/tools/definitions/video.d.ts.map +0 -1
- package/dist/tools/definitions/video.js.map +0 -1
- package/dist/tools/definitions.d.ts.map +0 -1
- package/dist/tools/definitions.js.map +0 -1
- package/dist/tools/handlers/ask-question.d.ts.map +0 -1
- package/dist/tools/handlers/ask-question.js.map +0 -1
- package/dist/tools/handlers/audio-video.d.ts.map +0 -1
- package/dist/tools/handlers/audio-video.js.map +0 -1
- package/dist/tools/handlers/auth.d.ts.map +0 -1
- package/dist/tools/handlers/auth.js.map +0 -1
- package/dist/tools/handlers/error-utils.d.ts.map +0 -1
- package/dist/tools/handlers/error-utils.js.map +0 -1
- package/dist/tools/handlers/gemini.d.ts.map +0 -1
- package/dist/tools/handlers/gemini.js.map +0 -1
- package/dist/tools/handlers/index.d.ts.map +0 -1
- package/dist/tools/handlers/index.js.map +0 -1
- package/dist/tools/handlers/notebook-creation.d.ts.map +0 -1
- package/dist/tools/handlers/notebook-creation.js.map +0 -1
- package/dist/tools/handlers/notebook-management.d.ts.map +0 -1
- package/dist/tools/handlers/notebook-management.js.map +0 -1
- package/dist/tools/handlers/session-management.d.ts.map +0 -1
- package/dist/tools/handlers/session-management.js.map +0 -1
- package/dist/tools/handlers/system.d.ts.map +0 -1
- package/dist/tools/handlers/system.js.map +0 -1
- package/dist/tools/handlers/types.d.ts.map +0 -1
- package/dist/tools/handlers/types.js.map +0 -1
- package/dist/tools/handlers/webhooks.d.ts.map +0 -1
- package/dist/tools/handlers/webhooks.js.map +0 -1
- package/dist/tools/handlers.d.ts +0 -666
- package/dist/tools/handlers.d.ts.map +0 -1
- package/dist/tools/handlers.js +0 -2929
- package/dist/tools/handlers.js.map +0 -1
- package/dist/tools/icons.d.ts.map +0 -1
- package/dist/tools/icons.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/audit-logger.d.ts.map +0 -1
- package/dist/utils/audit-logger.js.map +0 -1
- package/dist/utils/cert-pinning.d.ts +0 -97
- package/dist/utils/cert-pinning.d.ts.map +0 -1
- package/dist/utils/cert-pinning.js +0 -328
- package/dist/utils/cert-pinning.js.map +0 -1
- package/dist/utils/cleanup-manager.d.ts.map +0 -1
- package/dist/utils/cleanup-manager.js.map +0 -1
- package/dist/utils/cli-handler.d.ts.map +0 -1
- package/dist/utils/cli-handler.js.map +0 -1
- package/dist/utils/crypto.d.ts.map +0 -1
- package/dist/utils/crypto.js.map +0 -1
- package/dist/utils/file-lock.d.ts.map +0 -1
- package/dist/utils/file-lock.js.map +0 -1
- package/dist/utils/file-permissions.d.ts.map +0 -1
- package/dist/utils/file-permissions.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/page-utils.d.ts.map +0 -1
- package/dist/utils/page-utils.js.map +0 -1
- package/dist/utils/response-validator.d.ts.map +0 -1
- package/dist/utils/response-validator.js.map +0 -1
- package/dist/utils/secrets-scanner.d.ts.map +0 -1
- package/dist/utils/secrets-scanner.js.map +0 -1
- package/dist/utils/secure-memory.d.ts.map +0 -1
- package/dist/utils/secure-memory.js.map +0 -1
- package/dist/utils/security.d.ts.map +0 -1
- package/dist/utils/security.js.map +0 -1
- package/dist/utils/settings-manager.d.ts.map +0 -1
- package/dist/utils/settings-manager.js.map +0 -1
- package/dist/utils/stealth-utils.d.ts.map +0 -1
- package/dist/utils/stealth-utils.js.map +0 -1
- package/dist/utils/tool-validation.d.ts +0 -93
- package/dist/utils/tool-validation.d.ts.map +0 -1
- package/dist/utils/tool-validation.js +0 -277
- package/dist/utils/tool-validation.js.map +0 -1
- package/dist/webhooks/index.d.ts.map +0 -1
- package/dist/webhooks/index.js.map +0 -1
- package/dist/webhooks/types.d.ts.map +0 -1
- package/dist/webhooks/types.js.map +0 -1
- package/dist/webhooks/webhook-dispatcher.d.ts.map +0 -1
- package/dist/webhooks/webhook-dispatcher.js.map +0 -1
- package/docs/COMPLIANCE-SPEC.md +0 -1452
- package/docs/MCP-DIRECTORY-LISTINGS.md +0 -91
- package/docs/SECURITY-FORK-OPPORTUNITIES.md +0 -79
- package/docs/SECURITY_IMPLEMENTATION_PLAN.md +0 -437
- package/docs/archive/ISSUES-legacy-2026-04-24.md +0 -644
- package/docs/configuration.md +0 -94
- package/docs/dependency-risk.md +0 -25
- package/docs/improvement-sprint-2026.2.10.md +0 -210
- package/docs/testing-runbook.md +0 -166
- package/docs/tools.md +0 -34
- package/docs/troubleshooting.md +0 -59
- package/docs/usage-guide.md +0 -246
package/dist/utils/crypto.js
CHANGED
|
@@ -28,6 +28,20 @@ import { CONFIG } from "../config.js";
|
|
|
28
28
|
import { log } from "./logger.js";
|
|
29
29
|
import { audit } from "./audit-logger.js";
|
|
30
30
|
import { mkdirSecure, writeFileSecure, PERMISSION_MODES, } from "./file-permissions.js";
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when an encrypted file exists but cannot be decrypted (e.g. wrong
|
|
33
|
+
* key after rotation, corruption, or tampering). Distinct from a genuinely
|
|
34
|
+
* absent file (load() returns null), so callers can avoid overwriting
|
|
35
|
+
* good-but-undecryptable state.
|
|
36
|
+
*/
|
|
37
|
+
export class DecryptionError extends Error {
|
|
38
|
+
file;
|
|
39
|
+
constructor(message, file) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "DecryptionError";
|
|
42
|
+
this.file = file;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
31
45
|
/**
|
|
32
46
|
* Constants
|
|
33
47
|
*/
|
|
@@ -64,25 +78,6 @@ function hkdfDerive(ikm, salt, info, length) {
|
|
|
64
78
|
export function deriveKey(passphrase, salt, iterations = DEFAULT_PBKDF2_ITERATIONS) {
|
|
65
79
|
return crypto.pbkdf2Sync(passphrase, salt, iterations, KEY_LENGTH, "sha256");
|
|
66
80
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Generate a machine-derived key based on hardware/OS identifiers
|
|
69
|
-
*
|
|
70
|
-
* Note: This provides obscurity, not true security. It's a fallback
|
|
71
|
-
* when no user key is provided.
|
|
72
|
-
*/
|
|
73
|
-
export function getMachineKey() {
|
|
74
|
-
const components = [
|
|
75
|
-
os.hostname(),
|
|
76
|
-
os.platform(),
|
|
77
|
-
os.arch(),
|
|
78
|
-
os.cpus()[0]?.model || "unknown",
|
|
79
|
-
os.homedir(),
|
|
80
|
-
];
|
|
81
|
-
// Create a deterministic key from machine components
|
|
82
|
-
const combined = components.join("|");
|
|
83
|
-
const hash = crypto.createHash("sha256").update(combined).digest("hex");
|
|
84
|
-
return hash;
|
|
85
|
-
}
|
|
86
81
|
export function getOrCreateMachineKey(keyPath) {
|
|
87
82
|
if (fs.existsSync(keyPath)) {
|
|
88
83
|
const existingKey = fs.readFileSync(keyPath);
|
|
@@ -178,8 +173,10 @@ export function decryptPQ(encryptedData, recipientSecretKey) {
|
|
|
178
173
|
* Classical ChaCha20-Poly1305 encryption (fallback)
|
|
179
174
|
*/
|
|
180
175
|
export function encryptClassical(data, key) {
|
|
176
|
+
// No salt: the caller-supplied 256-bit key is used directly. The random
|
|
177
|
+
// 96-bit nonce provides per-ciphertext uniqueness. A salt was previously
|
|
178
|
+
// stored but never fed to any KDF (L3), so it was decorative and is dropped.
|
|
181
179
|
const nonce = crypto.randomBytes(NONCE_LENGTH);
|
|
182
|
-
const salt = crypto.randomBytes(SALT_LENGTH);
|
|
183
180
|
const cipher = crypto.createCipheriv(ALGORITHM, key, nonce, {
|
|
184
181
|
authTagLength: 16,
|
|
185
182
|
});
|
|
@@ -192,7 +189,6 @@ export function encryptClassical(data, key) {
|
|
|
192
189
|
version: CLASSICAL_VERSION,
|
|
193
190
|
algorithm: ALGORITHM,
|
|
194
191
|
nonce: nonce.toString("base64"),
|
|
195
|
-
salt: salt.toString("base64"),
|
|
196
192
|
ciphertext: ciphertextWithTag.toString("base64"),
|
|
197
193
|
};
|
|
198
194
|
}
|
|
@@ -214,8 +210,24 @@ export function decryptClassical(encryptedData, key) {
|
|
|
214
210
|
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
215
211
|
return decrypted;
|
|
216
212
|
}
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// LEGACY AES-GCM MIGRATION SHIM (L7) — bounded, remove after migration window.
|
|
215
|
+
//
|
|
216
|
+
// This block (LegacyAESEncryptedData, decryptLegacyAES, isLegacyFormat) exists
|
|
217
|
+
// ONLY to transparently upgrade pre-fork v1/v2 files that were encrypted with
|
|
218
|
+
// AES-GCM (detected by the presence of `iv` + `tag` fields) to the current
|
|
219
|
+
// ChaCha20-Poly1305 format on first read. It is intentionally isolated to these
|
|
220
|
+
// three symbols and adds one branch on the load/init paths.
|
|
221
|
+
//
|
|
222
|
+
// TODO(remove-after-migration-window): once all deployed installs have read
|
|
223
|
+
// their state at least once under a ChaCha20-Poly1305 build (so no AES-GCM
|
|
224
|
+
// files remain on disk), delete this shim along with the `isLegacyFormat`
|
|
225
|
+
// branches in initializePQKeys()/load(). No new data is ever written in this
|
|
226
|
+
// format — decrypt-only, one direction.
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
217
228
|
/**
|
|
218
|
-
* Decrypt legacy AES-GCM encrypted data (for migration)
|
|
229
|
+
* Decrypt legacy AES-GCM encrypted data (for migration). See migration shim
|
|
230
|
+
* note above — decrypt-only, scheduled for removal after the migration window.
|
|
219
231
|
*/
|
|
220
232
|
function decryptLegacyAES(encryptedData, key, pqSecretKey) {
|
|
221
233
|
let aesKey;
|
|
@@ -261,8 +273,16 @@ export class SecureStorage {
|
|
|
261
273
|
pqKeyPair = null;
|
|
262
274
|
initialized = false;
|
|
263
275
|
keyStorePath;
|
|
276
|
+
/**
|
|
277
|
+
* Captured at construction, before config.enabled can be mutated by
|
|
278
|
+
* initialize()/initializeClassicalKey(). Records whether the caller
|
|
279
|
+
* actually intended encryption. Used to fail closed: if encryption was
|
|
280
|
+
* expected but is unavailable, we refuse to write plaintext.
|
|
281
|
+
*/
|
|
282
|
+
encryptionExpected;
|
|
264
283
|
constructor(config) {
|
|
265
284
|
this.config = { ...getEncryptionConfig(), ...config };
|
|
285
|
+
this.encryptionExpected = this.config.enabled;
|
|
266
286
|
this.keyStorePath = path.join(process.env.NLMCP_CONFIG_DIR || CONFIG.configDir || path.join(os.homedir(), ".notebooklm-mcp"), "pq-keys.enc");
|
|
267
287
|
}
|
|
268
288
|
/**
|
|
@@ -287,9 +307,12 @@ export class SecureStorage {
|
|
|
287
307
|
this.initialized = true;
|
|
288
308
|
}
|
|
289
309
|
catch (error) {
|
|
310
|
+
// Fail closed: encryption was requested but initialization failed.
|
|
311
|
+
// Do NOT silently disable and fall through to plaintext writes —
|
|
312
|
+
// callers believe this data is encrypted.
|
|
290
313
|
log.error(` ❌ Failed to initialize encryption: ${error}`);
|
|
291
|
-
this.config.enabled = false;
|
|
292
314
|
await audit.security("encryption_init_failed", "error", { error: String(error) });
|
|
315
|
+
throw error;
|
|
293
316
|
}
|
|
294
317
|
}
|
|
295
318
|
/**
|
|
@@ -404,6 +427,13 @@ export class SecureStorage {
|
|
|
404
427
|
// Ensure directory exists with secure permissions
|
|
405
428
|
mkdirSecure(dir, PERMISSION_MODES.OWNER_FULL);
|
|
406
429
|
if (!this.config.enabled) {
|
|
430
|
+
// Fail closed: only write plaintext when encryption was genuinely
|
|
431
|
+
// never expected (e.g. NLMCP_ENCRYPTION_ENABLED=false). If encryption
|
|
432
|
+
// was expected but got disabled by an init failure, refuse.
|
|
433
|
+
if (this.encryptionExpected) {
|
|
434
|
+
throw new Error(`Refusing to save ${path.basename(filePath)} as plaintext: ` +
|
|
435
|
+
`encryption was expected but is unavailable (initialization failed)`);
|
|
436
|
+
}
|
|
407
437
|
// Save unencrypted
|
|
408
438
|
writeFileSecure(filePath, dataStr, PERMISSION_MODES.OWNER_READ_WRITE);
|
|
409
439
|
log.info(`📝 Saved (unencrypted): ${path.basename(filePath)}`);
|
|
@@ -423,13 +453,43 @@ export class SecureStorage {
|
|
|
423
453
|
log.info(`🔐 Saved with ChaCha20-Poly1305: ${path.basename(encryptedPath)}`);
|
|
424
454
|
}
|
|
425
455
|
else {
|
|
456
|
+
// Fail closed: no encryption keys are available. If encryption was
|
|
457
|
+
// expected, refuse rather than silently writing plaintext.
|
|
458
|
+
if (this.encryptionExpected) {
|
|
459
|
+
throw new Error(`Refusing to save ${path.basename(filePath)} as plaintext: ` +
|
|
460
|
+
`encryption was expected but no keys are available`);
|
|
461
|
+
}
|
|
426
462
|
// Save unencrypted as fallback
|
|
427
463
|
writeFileSecure(filePath, dataStr, PERMISSION_MODES.OWNER_READ_WRITE);
|
|
428
464
|
log.warning(`⚠️ Saved unencrypted (no keys): ${path.basename(filePath)}`);
|
|
429
465
|
return;
|
|
430
466
|
}
|
|
431
|
-
|
|
432
|
-
//
|
|
467
|
+
// L8: Write atomically via temp-file + rename. save() is invoked from
|
|
468
|
+
// inside load()'s migrate-on-read path (and callers may already hold the
|
|
469
|
+
// file lock on this same path, so we cannot re-acquire it here without
|
|
470
|
+
// self-deadlock). An atomic rename guarantees a crash or a concurrent
|
|
471
|
+
// reader never observes a partially written / truncated encrypted file:
|
|
472
|
+
// the destination either has the complete old contents or the complete
|
|
473
|
+
// new contents, never an intermediate state.
|
|
474
|
+
const tmpPath = `${encryptedPath}.tmp-${process.pid}-${crypto.randomBytes(6).toString("hex")}`;
|
|
475
|
+
try {
|
|
476
|
+
writeFileSecure(tmpPath, JSON.stringify(encrypted, null, 2), PERMISSION_MODES.OWNER_READ_WRITE);
|
|
477
|
+
fs.renameSync(tmpPath, encryptedPath);
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
// Best-effort cleanup of the temp file so we don't leave litter behind.
|
|
481
|
+
try {
|
|
482
|
+
if (fs.existsSync(tmpPath))
|
|
483
|
+
fs.unlinkSync(tmpPath);
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
/* ignore cleanup failure */
|
|
487
|
+
}
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
// Remove unencrypted and other encrypted versions if they exist. This runs
|
|
491
|
+
// only after the new file is durably in place, so a crash here leaves a
|
|
492
|
+
// valid (if duplicated) encrypted file rather than losing data.
|
|
433
493
|
const extensions = ["", ".enc", ".pqenc"];
|
|
434
494
|
for (const ext of extensions) {
|
|
435
495
|
const oldPath = filePath + ext;
|
|
@@ -471,7 +531,10 @@ export class SecureStorage {
|
|
|
471
531
|
type: "post-quantum",
|
|
472
532
|
error: String(error),
|
|
473
533
|
});
|
|
474
|
-
|
|
534
|
+
// Fail closed: the file exists but could not be decrypted. Throw a
|
|
535
|
+
// typed error so callers don't mistake this for a missing file and
|
|
536
|
+
// overwrite good-but-undecryptable state (e.g. after key rotation).
|
|
537
|
+
throw new DecryptionError(`Failed to decrypt ${pqEncryptedPath}: ${error instanceof Error ? error.message : String(error)}`, pqEncryptedPath);
|
|
475
538
|
}
|
|
476
539
|
}
|
|
477
540
|
// Check for classical encrypted version
|
|
@@ -509,7 +572,10 @@ export class SecureStorage {
|
|
|
509
572
|
type: "classical",
|
|
510
573
|
error: String(error),
|
|
511
574
|
});
|
|
512
|
-
|
|
575
|
+
// Fail closed: the file exists but could not be decrypted. Throw a
|
|
576
|
+
// typed error so callers don't mistake this for a missing file and
|
|
577
|
+
// overwrite good-but-undecryptable state (e.g. after key rotation).
|
|
578
|
+
throw new DecryptionError(`Failed to decrypt ${classicalEncryptedPath}: ${error instanceof Error ? error.message : String(error)}`, classicalEncryptedPath);
|
|
513
579
|
}
|
|
514
580
|
}
|
|
515
581
|
// Fall back to unencrypted version
|
|
@@ -613,4 +679,3 @@ export function getSecureStorage() {
|
|
|
613
679
|
}
|
|
614
680
|
return globalSecureStorage;
|
|
615
681
|
}
|
|
616
|
-
//# sourceMappingURL=crypto.js.map
|
|
@@ -26,6 +26,21 @@ export interface LockOptions {
|
|
|
26
26
|
/** Lock considered stale after this time (ms) */
|
|
27
27
|
staleThreshold?: number;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Single shared stale-lock threshold (L15).
|
|
31
|
+
*
|
|
32
|
+
* The async FileLock util and the synchronous shutdown-flush path in
|
|
33
|
+
* audit-logger.ts (writeWithSyncLock) lock the SAME audit-*.jsonl files, so they
|
|
34
|
+
* MUST agree on when a lock is stale. They previously diverged (900_000ms here vs
|
|
35
|
+
* a hardcoded 30_000ms in the sync path): the sync path could steal a lock at 30s
|
|
36
|
+
* that the async owner still considered live (900s), and both would then write the
|
|
37
|
+
* same audit file concurrently — corrupting the very log the lock protects.
|
|
38
|
+
*
|
|
39
|
+
* Unified UP to 900_000ms (15 min): losing a best-effort shutdown flush (the sync
|
|
40
|
+
* path only waits 10s total anyway) is strictly less bad than corrupting the audit
|
|
41
|
+
* log. Overridable via NLMCP_LOCK_STALE_MS.
|
|
42
|
+
*/
|
|
43
|
+
export declare const STALE_LOCK_THRESHOLD_MS: number;
|
|
29
44
|
/**
|
|
30
45
|
* File Lock Class
|
|
31
46
|
*
|
|
@@ -76,4 +91,3 @@ export declare function isLocked(filePath: string, staleThreshold?: number): boo
|
|
|
76
91
|
* Only use when you're certain the lock is orphaned.
|
|
77
92
|
*/
|
|
78
93
|
export declare function forceUnlock(filePath: string): boolean;
|
|
79
|
-
//# sourceMappingURL=file-lock.d.ts.map
|
package/dist/utils/file-lock.js
CHANGED
|
@@ -26,13 +26,28 @@ function parseIntegerEnv(name, fallback) {
|
|
|
26
26
|
const parsed = Number.parseInt(raw, 10);
|
|
27
27
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Single shared stale-lock threshold (L15).
|
|
31
|
+
*
|
|
32
|
+
* The async FileLock util and the synchronous shutdown-flush path in
|
|
33
|
+
* audit-logger.ts (writeWithSyncLock) lock the SAME audit-*.jsonl files, so they
|
|
34
|
+
* MUST agree on when a lock is stale. They previously diverged (900_000ms here vs
|
|
35
|
+
* a hardcoded 30_000ms in the sync path): the sync path could steal a lock at 30s
|
|
36
|
+
* that the async owner still considered live (900s), and both would then write the
|
|
37
|
+
* same audit file concurrently — corrupting the very log the lock protects.
|
|
38
|
+
*
|
|
39
|
+
* Unified UP to 900_000ms (15 min): losing a best-effort shutdown flush (the sync
|
|
40
|
+
* path only waits 10s total anyway) is strictly less bad than corrupting the audit
|
|
41
|
+
* log. Overridable via NLMCP_LOCK_STALE_MS.
|
|
42
|
+
*/
|
|
43
|
+
export const STALE_LOCK_THRESHOLD_MS = parseIntegerEnv("NLMCP_LOCK_STALE_MS", 900000);
|
|
29
44
|
/**
|
|
30
45
|
* Default lock options
|
|
31
46
|
*/
|
|
32
47
|
const DEFAULT_OPTIONS = {
|
|
33
48
|
timeout: parseIntegerEnv("NLMCP_LOCK_TIMEOUT_MS", 10000),
|
|
34
49
|
retryInterval: 100,
|
|
35
|
-
staleThreshold:
|
|
50
|
+
staleThreshold: STALE_LOCK_THRESHOLD_MS,
|
|
36
51
|
};
|
|
37
52
|
/**
|
|
38
53
|
* Generate unique lock ID
|
|
@@ -69,33 +84,53 @@ export class FileLock {
|
|
|
69
84
|
}
|
|
70
85
|
while (Date.now() - startTime < opts.timeout) {
|
|
71
86
|
try {
|
|
72
|
-
// Check
|
|
73
|
-
|
|
87
|
+
// Check for a stale or corrupted lock. Never blind-unlink by mere
|
|
88
|
+
// existence: a concurrent process may have replaced a stale lock with
|
|
89
|
+
// its own fresh one. Use compare-and-delete — re-read the lock
|
|
90
|
+
// immediately before unlinking and only remove the EXACT bytes we
|
|
91
|
+
// observed. The atomic `wx` create below is the real backstop: if the
|
|
92
|
+
// file is recreated between our delete and our create, it EEXISTs and
|
|
93
|
+
// we simply loop.
|
|
94
|
+
let observed = null;
|
|
95
|
+
try {
|
|
96
|
+
observed = fs.readFileSync(this.lockPath, "utf-8");
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
// ENOENT (no lock present) is the common, expected case — fall through
|
|
100
|
+
// to the create attempt below.
|
|
101
|
+
if (err.code !== "ENOENT") {
|
|
102
|
+
log.debug(`file-lock: reading lock file in acquire: ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (observed !== null) {
|
|
106
|
+
let removable = false;
|
|
74
107
|
try {
|
|
75
|
-
const
|
|
76
|
-
const existing = JSON.parse(content);
|
|
108
|
+
const existing = JSON.parse(observed);
|
|
77
109
|
const age = Date.now() - existing.timestamp;
|
|
78
110
|
if (age > opts.staleThreshold) {
|
|
79
|
-
// Lock is stale, remove it
|
|
80
111
|
log.warning(`🔓 Removing stale lock (age: ${Math.round(age / 1000)}s, pid: ${existing.pid})`);
|
|
81
|
-
|
|
82
|
-
fs.unlinkSync(this.lockPath);
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
log.debug(`file-lock: removing stale lock file in acquire: ${err instanceof Error ? err.message : String(err)}`);
|
|
86
|
-
// Ignore if another process already removed it
|
|
87
|
-
}
|
|
112
|
+
removable = true;
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
catch (err) {
|
|
91
116
|
log.debug(`file-lock: reading/parsing lock file content in acquire: ${err instanceof Error ? err.message : String(err)}`);
|
|
92
|
-
// Corrupted lock file,
|
|
117
|
+
// Corrupted lock file — eligible for removal, but still only if the
|
|
118
|
+
// exact corrupted bytes are unchanged when we re-read below.
|
|
119
|
+
removable = true;
|
|
120
|
+
}
|
|
121
|
+
if (removable) {
|
|
93
122
|
try {
|
|
94
|
-
|
|
123
|
+
// Compare-and-delete: re-read and confirm the content is byte-for-byte
|
|
124
|
+
// identical to what we observed before unlinking, so we never delete
|
|
125
|
+
// a different process's freshly-written lock.
|
|
126
|
+
const reread = fs.readFileSync(this.lockPath, "utf-8");
|
|
127
|
+
if (reread === observed) {
|
|
128
|
+
fs.unlinkSync(this.lockPath);
|
|
129
|
+
}
|
|
95
130
|
}
|
|
96
131
|
catch (err) {
|
|
97
|
-
log.debug(`file-lock:
|
|
98
|
-
//
|
|
132
|
+
log.debug(`file-lock: compare-and-delete stale lock in acquire: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
// Another process already changed or removed it — let the wx create decide.
|
|
99
134
|
}
|
|
100
135
|
}
|
|
101
136
|
}
|
|
@@ -140,54 +175,28 @@ export class FileLock {
|
|
|
140
175
|
release() {
|
|
141
176
|
if (!this.acquired)
|
|
142
177
|
return;
|
|
143
|
-
const tempPath = `${this.lockPath}.${this.lockId}.release`;
|
|
144
178
|
try {
|
|
145
|
-
if
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (finalLock.lockId === this.lockId) {
|
|
158
|
-
fs.unlinkSync(this.lockPath);
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
log.warning(`⚠️ Lock changed before release completed, not deleting`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
log.warning(`⚠️ Lock owned by different process, not releasing`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
log.debug(`file-lock: verifying and releasing lock file in release: ${err instanceof Error ? err.message : String(err)}`);
|
|
171
|
-
// Ignore errors during release
|
|
172
|
-
}
|
|
179
|
+
// Compare-and-delete: read the lock once and only remove it if it is
|
|
180
|
+
// still ours. If another process validly stole the lock after the stale
|
|
181
|
+
// threshold, we leave it untouched rather than clobbering it (the old
|
|
182
|
+
// temp-file rename dance could overwrite a newer owner's lock with our
|
|
183
|
+
// stale content).
|
|
184
|
+
const content = fs.readFileSync(this.lockPath, "utf-8");
|
|
185
|
+
const existing = JSON.parse(content);
|
|
186
|
+
if (existing.lockId === this.lockId) {
|
|
187
|
+
fs.unlinkSync(this.lockPath);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
log.warning(`⚠️ Lock owned by different process, not releasing`);
|
|
173
191
|
}
|
|
174
192
|
}
|
|
175
193
|
catch (err) {
|
|
176
|
-
|
|
177
|
-
|
|
194
|
+
// ENOENT (already gone), parse errors, or a concurrent unlink — nothing to do.
|
|
195
|
+
log.debug(`file-lock: compare-and-delete in release: ${err instanceof Error ? err.message : String(err)}`);
|
|
178
196
|
}
|
|
179
197
|
finally {
|
|
180
|
-
|
|
181
|
-
if (fs.existsSync(tempPath)) {
|
|
182
|
-
fs.unlinkSync(tempPath);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch (err) {
|
|
186
|
-
log.debug(`file-lock: removing temp file in release finally block: ${err instanceof Error ? err.message : String(err)}`);
|
|
187
|
-
// Ignore temp cleanup errors
|
|
188
|
-
}
|
|
198
|
+
this.acquired = false;
|
|
189
199
|
}
|
|
190
|
-
this.acquired = false;
|
|
191
200
|
}
|
|
192
201
|
/**
|
|
193
202
|
* Check if lock is acquired
|
|
@@ -290,4 +299,3 @@ export function forceUnlock(filePath) {
|
|
|
290
299
|
return false;
|
|
291
300
|
}
|
|
292
301
|
}
|
|
293
|
-
//# sourceMappingURL=file-lock.js.map
|
|
@@ -249,13 +249,42 @@ export function writeFileSecure(filePath, content, mode = PERMISSION_MODES.OWNER
|
|
|
249
249
|
* @param mode - Unix permission mode (default: 0o600)
|
|
250
250
|
*/
|
|
251
251
|
export function appendFileSecure(filePath, content, mode = PERMISSION_MODES.OWNER_READ_WRITE) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
252
|
+
// Ensure parent directory exists (matches writeFileSecure behaviour)
|
|
253
|
+
mkdirSecure(path.dirname(filePath));
|
|
254
|
+
if (isWindows) {
|
|
255
|
+
// O_NOFOLLOW / uid ownership are Unix concepts. On Windows, fall back to
|
|
256
|
+
// the existing create-then-ACL path used by writeFileSecure.
|
|
257
|
+
const existed = fs.existsSync(filePath);
|
|
258
258
|
fs.appendFileSync(filePath, content);
|
|
259
|
+
if (!existed) {
|
|
260
|
+
setWindowsFilePermissions(filePath, true);
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
// Unix: open in append mode atomically (creates if absent) with O_NOFOLLOW
|
|
265
|
+
// so a pre-planted symlink at filePath causes the open to fail (ELOOP)
|
|
266
|
+
// rather than redirecting our write to an attacker-chosen target. Avoid
|
|
267
|
+
// existsSync entirely — it is itself a TOCTOU. The single open both creates
|
|
268
|
+
// (with `mode`) and appends, closing the create/append race.
|
|
269
|
+
const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND | fs.constants.O_NOFOLLOW;
|
|
270
|
+
const fd = fs.openSync(filePath, flags, mode);
|
|
271
|
+
try {
|
|
272
|
+
// fstat the open descriptor (not the path) — symlink-safe — and confirm we
|
|
273
|
+
// are writing to a regular file owned by the current user before writing.
|
|
274
|
+
const stat = fs.fstatSync(fd);
|
|
275
|
+
if (!stat.isFile()) {
|
|
276
|
+
throw new Error(`Refusing to append: ${filePath} is not a regular file`);
|
|
277
|
+
}
|
|
278
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid()) {
|
|
279
|
+
throw new Error(`Refusing to append: ${filePath} is not owned by the current user`);
|
|
280
|
+
}
|
|
281
|
+
// Re-assert secure permissions on the fd (symlink-safe). `mode` only applies
|
|
282
|
+
// on creation, so this also tightens any weakened pre-existing permissions.
|
|
283
|
+
fs.fchmodSync(fd, mode);
|
|
284
|
+
fs.writeSync(fd, Buffer.isBuffer(content) ? content : Buffer.from(content));
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
fs.closeSync(fd);
|
|
259
288
|
}
|
|
260
289
|
}
|
|
261
290
|
/**
|
|
@@ -271,4 +300,3 @@ export function getPlatformInfo() {
|
|
|
271
300
|
supportsWindowsACLs: isWindows,
|
|
272
301
|
};
|
|
273
302
|
}
|
|
274
|
-
//# sourceMappingURL=file-permissions.js.map
|
package/dist/utils/logger.d.ts
CHANGED
package/dist/utils/logger.js
CHANGED
package/dist/utils/page-utils.js
CHANGED
|
@@ -10,21 +10,23 @@
|
|
|
10
10
|
* Based on the Python implementation from page_utils.py
|
|
11
11
|
*/
|
|
12
12
|
import { log } from "./logger.js";
|
|
13
|
+
import { validateResponse } from "./response-validator.js";
|
|
13
14
|
import { RESPONSE_SELECTORS, getSelectors } from "../notebook-creation/selectors.js";
|
|
14
15
|
// ============================================================================
|
|
15
16
|
// Helper Functions
|
|
16
17
|
// ============================================================================
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
+
* Sanitize untrusted assistant/document text before it is returned to the
|
|
20
|
+
* calling model. Notebook sources are arbitrary documents, so extracted text
|
|
21
|
+
* is a prompt-injection conduit; route it through the shared response
|
|
22
|
+
* validator (same mechanism used by the ask_question handler).
|
|
19
23
|
*/
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
hash = (hash << 5) - hash + char;
|
|
25
|
-
hash = hash & hash; // Convert to 32bit integer
|
|
24
|
+
async function sanitizeExtractedText(text) {
|
|
25
|
+
const validation = await validateResponse(text);
|
|
26
|
+
if (!validation.safe) {
|
|
27
|
+
log.warning("🛡️ Suspicious content detected in extracted page text — sanitized");
|
|
26
28
|
}
|
|
27
|
-
return
|
|
29
|
+
return validation.sanitized;
|
|
28
30
|
}
|
|
29
31
|
// ============================================================================
|
|
30
32
|
// Main Functions
|
|
@@ -34,7 +36,8 @@ function hashString(str) {
|
|
|
34
36
|
* Returns null if no response found
|
|
35
37
|
*/
|
|
36
38
|
export async function snapshotLatestResponse(page) {
|
|
37
|
-
|
|
39
|
+
const text = await extractLatestText(page, new Set(), false, 0);
|
|
40
|
+
return text === null ? null : await sanitizeExtractedText(text);
|
|
38
41
|
}
|
|
39
42
|
/**
|
|
40
43
|
* Snapshot ALL existing assistant response texts
|
|
@@ -52,7 +55,7 @@ export async function snapshotAllResponses(page) {
|
|
|
52
55
|
if (textElement) {
|
|
53
56
|
const text = await textElement.innerText();
|
|
54
57
|
if (text && text.trim()) {
|
|
55
|
-
allTexts.push(text.trim());
|
|
58
|
+
allTexts.push(await sanitizeExtractedText(text.trim()));
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -121,15 +124,17 @@ export async function waitForLatestAnswer(page, options = {}) {
|
|
|
121
124
|
const { question = "", timeoutMs = 120000, pollIntervalMs = 1000, ignoreTexts = [], debug = false, } = options;
|
|
122
125
|
const deadline = Date.now() + timeoutMs;
|
|
123
126
|
const sanitizedQuestion = question.trim().toLowerCase();
|
|
124
|
-
// Track ALL known texts
|
|
125
|
-
|
|
127
|
+
// Track ALL known texts by their trimmed string value. Storing the full
|
|
128
|
+
// strings (rather than a 32-bit hash) avoids hash collisions that could
|
|
129
|
+
// cause a genuinely new answer to be treated as "seen" and hang to timeout.
|
|
130
|
+
const knownTexts = new Set();
|
|
126
131
|
for (const text of ignoreTexts) {
|
|
127
132
|
if (typeof text === "string" && text.trim()) {
|
|
128
|
-
|
|
133
|
+
knownTexts.add(text.trim());
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
if (debug) {
|
|
132
|
-
log.debug(`🔍 [DEBUG] Waiting for NEW answer. Ignoring ${
|
|
137
|
+
log.debug(`🔍 [DEBUG] Waiting for NEW answer. Ignoring ${knownTexts.size} known responses`);
|
|
133
138
|
}
|
|
134
139
|
let pollCount = 0;
|
|
135
140
|
let lastCandidate = null;
|
|
@@ -156,7 +161,7 @@ export async function waitForLatestAnswer(page, options = {}) {
|
|
|
156
161
|
// Ignore errors checking thinking state
|
|
157
162
|
}
|
|
158
163
|
// Extract latest NEW text
|
|
159
|
-
const candidate = await extractLatestText(page,
|
|
164
|
+
const candidate = await extractLatestText(page, knownTexts, debug, pollCount);
|
|
160
165
|
if (candidate) {
|
|
161
166
|
const normalized = candidate.trim();
|
|
162
167
|
if (normalized) {
|
|
@@ -166,7 +171,7 @@ export async function waitForLatestAnswer(page, options = {}) {
|
|
|
166
171
|
if (debug) {
|
|
167
172
|
log.debug("🔍 [DEBUG] Found question echo, ignoring");
|
|
168
173
|
}
|
|
169
|
-
|
|
174
|
+
knownTexts.add(normalized); // Mark as seen
|
|
170
175
|
await page.waitForTimeout(pollIntervalMs);
|
|
171
176
|
continue;
|
|
172
177
|
}
|
|
@@ -193,7 +198,7 @@ export async function waitForLatestAnswer(page, options = {}) {
|
|
|
193
198
|
if (debug) {
|
|
194
199
|
log.debug(`✅ [DEBUG] Returning stable answer (${normalized.length} chars)`);
|
|
195
200
|
}
|
|
196
|
-
return normalized;
|
|
201
|
+
return await sanitizeExtractedText(normalized);
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
}
|
|
@@ -206,31 +211,31 @@ export async function waitForLatestAnswer(page, options = {}) {
|
|
|
206
211
|
}
|
|
207
212
|
/**
|
|
208
213
|
* Extract the latest NEW response text from the page
|
|
209
|
-
*
|
|
214
|
+
* Compares against the set of already-seen trimmed response strings
|
|
210
215
|
*
|
|
211
216
|
* @param page Playwright page instance
|
|
212
|
-
* @param
|
|
217
|
+
* @param knownTexts Set of already-seen (trimmed) response texts
|
|
213
218
|
* @param debug Enable debug logging
|
|
214
219
|
* @param pollCount Current poll number (for conditional logging)
|
|
215
220
|
* @returns First NEW response text found, or null
|
|
216
221
|
*/
|
|
217
|
-
async function extractLatestText(page,
|
|
222
|
+
async function extractLatestText(page, knownTexts, debug, pollCount) {
|
|
218
223
|
// Try the primary selector first (most specific for NotebookLM)
|
|
219
224
|
const primarySelector = ".to-user-container";
|
|
220
225
|
try {
|
|
221
226
|
const containers = await page.$$(primarySelector);
|
|
222
227
|
const totalContainers = containers.length;
|
|
223
228
|
// Early exit if no new containers possible
|
|
224
|
-
if (totalContainers <=
|
|
229
|
+
if (totalContainers <= knownTexts.size) {
|
|
225
230
|
if (debug && pollCount % 5 === 0) {
|
|
226
|
-
log.dim(`⏭️ [EXTRACT] No new containers (${totalContainers} total, ${
|
|
231
|
+
log.dim(`⏭️ [EXTRACT] No new containers (${totalContainers} total, ${knownTexts.size} known)`);
|
|
227
232
|
}
|
|
228
233
|
return null;
|
|
229
234
|
}
|
|
230
235
|
if (containers.length > 0) {
|
|
231
236
|
// Only log every 5th poll to reduce noise
|
|
232
237
|
if (debug && pollCount % 5 === 0) {
|
|
233
|
-
log.dim(`🔍 [EXTRACT] Scanning ${totalContainers} containers (${
|
|
238
|
+
log.dim(`🔍 [EXTRACT] Scanning ${totalContainers} containers (${knownTexts.size} known)`);
|
|
234
239
|
}
|
|
235
240
|
let skipped = 0;
|
|
236
241
|
let empty = 0;
|
|
@@ -242,9 +247,9 @@ async function extractLatestText(page, knownHashes, debug, pollCount) {
|
|
|
242
247
|
if (textElement) {
|
|
243
248
|
const text = await textElement.innerText();
|
|
244
249
|
if (text && text.trim()) {
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
if (!
|
|
250
|
+
// Exact-string comparison against seen responses (no hash
|
|
251
|
+
// collisions that could drop a genuinely new answer).
|
|
252
|
+
if (!knownTexts.has(text.trim())) {
|
|
248
253
|
log.success(`✅ [EXTRACT] Found NEW text in container[${idx}]: ${text.trim().length} chars`);
|
|
249
254
|
return text.trim();
|
|
250
255
|
}
|
|
@@ -305,7 +310,7 @@ async function extractLatestText(page, knownHashes, debug, pollCount) {
|
|
|
305
310
|
container = element;
|
|
306
311
|
}
|
|
307
312
|
const text = await container.innerText();
|
|
308
|
-
if (text && text.trim() && !
|
|
313
|
+
if (text && text.trim() && !knownTexts.has(text.trim())) {
|
|
309
314
|
return text.trim();
|
|
310
315
|
}
|
|
311
316
|
}
|
|
@@ -424,4 +429,3 @@ export default {
|
|
|
424
429
|
countResponseElements,
|
|
425
430
|
waitForLatestAnswer,
|
|
426
431
|
};
|
|
427
|
-
//# sourceMappingURL=page-utils.js.map
|