Unofficial Python API and agentic skill for Google NotebookLM. Full programmatic access to NotebookLM's features—including capabilities the web UI doesn't expose—via Python, CLI, and AI agents like Claude Code, Codex, and OpenClaw.
8902 matches across 16 categories. Click a row to expand file-level details.
| Severity | File | Line | Snippet |
|---|---|---|---|
| MEDIUM | tests/cassette_patterns.py | 449 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 451 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 458 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 460 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 479 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 481 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 488 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 490 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 498 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 500 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 507 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 509 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 518 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 520 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 571 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 573 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 583 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 585 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 603 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 605 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 610 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 612 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 104 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 106 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 222 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 224 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 275 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 277 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 291 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 293 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 331 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 333 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 373 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 375 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 537 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 539 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 552 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 554 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 625 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 627 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 649 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 651 | # ------------------------------------------------------------------------- |
| MEDIUM | tests/cassette_patterns.py | 664 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 666 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 909 | # ============================================================================= |
| MEDIUM | tests/cassette_patterns.py | 911 | # ============================================================================= |
| MEDIUM | tests/vcr_config.py | 271 | # ============================================================================= |
| MEDIUM | tests/vcr_config.py | 273 | # ============================================================================= |
| MEDIUM | tests/vcr_config.py | 656 | # ============================================================================= |
| MEDIUM | tests/vcr_config.py | 658 | # ============================================================================= |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 378 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 380 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 408 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 410 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 447 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 449 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 479 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 481 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 587 | # --------------------------------------------------------------------------- |
| MEDIUM | tests/_lint/test_session_runtime_boundaries.py | 589 | # --------------------------------------------------------------------------- |
| 1689 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/conftest.py | 184 | def pytest_collection_modifyitems(config, items): |
| LOW | tests/conftest.py | 241 | def mock_list_notebooks_response(): |
| LOW | tests/cassette_patterns.py | 942 | def synthetic_error_cassette_name(mode: str, slug: str) -> str: |
| LOW | tests/cassette_patterns.py | 959 | def build_synthetic_error_response( |
| LOW | tests/vcr_config.py | 173 | def _substitute_synthetic_error(response: dict[str, Any]) -> dict[str, Any]: |
| LOW | tests/_lint/test_integration_allow_no_vcr_allowlist.py | 9 | def test_integration_allow_no_vcr_taxonomy_policy_is_current() -> None: |
| LOW | tests/_lint/test_no_session.py | 32 | def _is_deleted_module_string(value: str) -> bool: |
| LOW | tests/_lint/test_no_session.py | 108 | def test_no_deleted_session_surface_in_source_or_tests() -> None: |
| LOW | tests/_lint/test_no_session.py | 116 | def test_scan_source_catches_static_and_dynamic_deleted_surface() -> None: |
| LOW | tests/_lint/test_no_session.py | 139 | def test_scan_source_allows_surviving_sibling_modules() -> None: |
| LOW | tests/_lint/test_no_module_shadowing.py | 78 | def test_cmd_module_imports_as_real_module(name: str) -> None: |
| LOW | tests/_lint/test_no_module_shadowing.py | 92 | def test_click_group_public_alias_preserved(public_name: str) -> None: |
| LOW | tests/_lint/test_error_handler_allowlist.py | 51 | def _click_exception_call_sites() -> set[AllowedSite]: |
| LOW | tests/_lint/test_error_handler_allowlist.py | 64 | def test_raw_system_exit_sites_are_error_handler_owned_or_allowlisted() -> None: |
| LOW | tests/_lint/test_error_handler_allowlist.py | 78 | def test_click_exception_sites_match_input_validation_allowlist() -> None: |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 267 | def test_linter_catches_each_context(source: str, expected: list[tuple[int, str, str]]) -> None: |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 272 | def test_linter_does_not_flag_non_forbidden_attrs() -> None: |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 297 | def test_linter_dynamic_access_known_negatives(source: str) -> None: |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 307 | def test_is_carve_out_for_runtime_lifecycle() -> None: |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 318 | def test_no_session_compat_bridges() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 383 | def test_lifecycle_host_symbol_does_not_appear_in_src() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 413 | def test_no_cast_to_lifecycle_host_in_src() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 452 | def test_refresh_auth_core_symbol_does_not_appear_in_src() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 484 | def test_auth_session_module_has_no_host_protocol_residue() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 235 | def _protocol_class_declares_kernel(class_def: ast.ClassDef) -> bool: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 310 | def _format_receiver_for_diagnostic(receiver: ast.expr) -> str: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 348 | def _moved_session_symbol_alias_violations(tree: ast.AST, *, rel: str) -> list[str]: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 365 | def test_moved_session_symbols_are_not_reached_through_session_module_aliases() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 621 | def test_find_symbol_appearances_catches_each_context( |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 634 | def test_find_symbol_appearances_ignores_unrelated_names() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 668 | def test_cast_target_extraction(source: str, expected_target: str | None) -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 686 | def test_protocol_class_declares_kernel_positive() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 696 | def test_protocol_class_declares_kernel_skips_non_protocol() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 707 | def test_imports_session_class_catches_both_shapes() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 759 | def test_is_coordinator_receiver_covers_both_shapes(source: str, is_coordinator: bool) -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 787 | def test_format_receiver_for_diagnostic_shapes() -> None: |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 800 | def test_moved_session_symbol_alias_guard_catches_synthetic_alias() -> None: |
| LOW | tests/_lint/test_client_composition.py | 39 | def test_features_receive_specific_collaborators_not_whole_client() -> None: |
| LOW | tests/_lint/test_client_composition.py | 60 | def test_notebooklm_client_does_not_inline_composition_holder_state() -> None: |
| LOW | tests/_lint/test_client_composition.py | 77 | def test_client_composed_does_not_expose_collaborators_alias() -> None: |
| LOW | tests/_lint/test_login_init_is_reexport_only.py | 80 | def test_login_init_reexport_only() -> None: |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 264 | def test_no_forbidden_monkeypatches_outside_allowlist() -> None: |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 49 | def test_rpc_callable_guard_helpers_cover_unpacked_assignments_and_import_aliases() -> None: |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 73 | def test_retired_rpc_callable_names_do_not_return() -> None: |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 95 | def test_rpc_callback_stays_upload_only() -> None: |
| LOW | tests/_lint/test_auth_source_consolidation.py | 32 | def test_auth_json_env_literal_is_centralized() -> None: |
| LOW | tests/_lint/test_no_core_imports.py | 125 | def test_no_runtime_core_imports(rel_path: str, extra_banned: frozenset[str]) -> None: |
| LOW | …sts/_lint/test_no_deprecated_public_rpc_call_kwargs.py | 44 | def _is_public_client_rpc_call(node: ast.Call) -> bool: |
| LOW | …sts/_lint/test_no_deprecated_public_rpc_call_kwargs.py | 134 | def test_no_unauthorized_deprecated_public_rpc_call_kwargs() -> None: |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 147 | def _is_primitive_construction( |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 285 | def test_every_asyncio_primitive_is_loop_affinity_guarded() -> None: |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 324 | def test_loop_affinity_allowlist_has_no_stale_entries() -> None: |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 340 | def test_loop_affinity_followup_entries_reference_a_tracking_issue() -> None: |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 373 | def test_detector_handles_all_import_styles() -> None: |
| LOW | tests/_helpers/client_factory.py | 32 | def build_client_shell_for_tests( |
| LOW | tests/unit/test_middleware_context_contract.py | 129 | def _record_context_dict_literal_assignment( |
| LOW | tests/unit/test_middleware_context_contract.py | 150 | def test_allowed_rpc_context_keys_match_adr_vocabulary() -> None: |
| LOW | tests/unit/test_middleware_context_contract.py | 163 | def test_context_literal_visitor_records_update_keyword_keys() -> None: |
| LOW | tests/unit/test_middleware_context_contract.py | 178 | def test_production_context_literal_keys_stay_in_allowed_vocabulary() -> None: |
| LOW | tests/unit/test_artifacts_helpers.py | 30 | def test_extract_data_table_rows_happy_path() -> None: |
| 5668 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/cassette_patterns.py | 100 | |
| LOW | tests/_lint/test_integration_allow_no_vcr_allowlist.py | 3 | |
| LOW | tests/_lint/test_no_session.py | 3 | |
| LOW | tests/_lint/test_no_module_shadowing.py | 31 | |
| LOW | tests/_lint/test_error_handler_allowlist.py | 3 | |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 39 | |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 97 | |
| LOW | tests/_lint/test_client_composition.py | 3 | |
| LOW | tests/_lint/test_login_init_is_reexport_only.py | 6 | |
| LOW | tests/_lint/test_login_package_dag.py | 8 | |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 57 | |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 3 | |
| LOW | tests/_lint/test_auth_source_consolidation.py | 10 | |
| LOW | tests/_lint/test_no_core_imports.py | 28 | |
| LOW | …sts/_lint/test_no_deprecated_public_rpc_call_kwargs.py | 9 | |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 32 | |
| LOW | tests/_helpers/client_factory.py | 3 | |
| LOW | tests/unit/test_middleware_context_contract.py | 3 | |
| LOW | tests/unit/test_artifacts_helpers.py | 12 | |
| LOW | tests/unit/test_json_stdout_purity.py | 19 | |
| LOW | tests/unit/test_drain_tracker_hooks.py | 18 | |
| LOW | tests/unit/test_refresh_lock_registry.py | 11 | |
| LOW | tests/unit/test_from_storage_dual_protocol.py | 18 | |
| LOW | tests/unit/test_firefox_containers.py | 12 | |
| LOW | tests/unit/test_vcr_scrubbing.py | 30 | |
| LOW | tests/unit/test_auth_instance_invariant.py | 3 | |
| LOW | tests/unit/test_vcr_config.py | 20 | |
| LOW | tests/unit/test_source_upload_coverage.py | 11 | |
| LOW | tests/unit/test_tier_enforcement_hook.py | 32 | |
| LOW | tests/unit/test_auth_refresh_retry.py | 12 | |
| LOW | tests/unit/test_streaming_chat_wire.py | 3 | |
| LOW | tests/unit/test_streaming_chat_wire.py | 570 | |
| LOW | tests/unit/test_concurrency_refresh_race.py | 58 | |
| LOW | tests/unit/test_middleware_chain_host.py | 28 | |
| LOW | tests/unit/test_cli_boundary.py | 30 | |
| LOW | tests/unit/test_with_client_handle_errors.py | 20 | |
| LOW | tests/unit/test_logging.py | 3 | |
| LOW | tests/unit/test_check_rpc_health.py | 19 | |
| LOW | tests/unit/test_refresh_state_machine.py | 14 | |
| LOW | tests/unit/test_logging_correlation.py | 3 | |
| LOW | tests/unit/test_download_result.py | 3 | |
| LOW | tests/unit/test_chat_passage_resolver.py | 15 | |
| LOW | tests/unit/test_chat_helpers.py | 11 | |
| LOW | tests/unit/test_auth_psidts_recovery.py | 12 | |
| LOW | tests/unit/test_mind_maps_api.py | 3 | |
| LOW | tests/unit/test_error_injection_middleware.py | 41 | |
| LOW | tests/unit/test_public_surface.py | 26 | |
| LOW | tests/unit/test_chain_wiring.py | 27 | |
| LOW | tests/unit/test_interactive_mind_map.py | 11 | |
| LOW | tests/unit/test_playwright_marker_hook.py | 22 | |
| LOW | tests/unit/test_e2e_conftest_options.py | 11 | |
| LOW | tests/unit/test_auth_account_coverage.py | 13 | |
| LOW | tests/unit/test_runtime_auth.py | 35 | |
| LOW | tests/unit/test_cookie_domain_split.py | 21 | |
| LOW | tests/unit/test_notebook_metadata.py | 3 | |
| LOW | tests/unit/test_download_url.py | 11 | |
| LOW | tests/unit/test_tier_13_all_exports.py | 15 | |
| LOW | tests/unit/test_profile_atomic_write.py | 38 | |
| LOW | tests/unit/test_type_boundaries.py | 3 | |
| LOW | tests/unit/test_atomic_io.py | 14 | |
| 693 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | tests/cassette_patterns.py | 122 | Re-derive ``<count>`` prefixes in a chunked response body. Google's batchexecute responses are framed as alternatin |
| HIGH | tests/cassette_patterns.py | 962 | Return a ``(status_code, body, headers)`` triple for a synthetic error. The shape is intentionally minimal; the cli |
| HIGH | src/notebooklm/_sharing.py | 148 | Share notebook with a user. Args: notebook_id: The notebook ID. email: User's email add |
| HIGH | src/notebooklm/_research.py | 331 | Start a research session. Args: notebook_id: The notebook ID. query: The research query |
| HIGH | src/notebooklm/_research.py | 470 | Poll until research reaches a terminal state or times out. When the first poll returns a concrete ``task_id``, |
| HIGH | src/notebooklm/_reqid_counter.py | 142 | Atomically increment the counter and return the new value. Args: step: Increment applied to the cou |
| HIGH | src/notebooklm/paths.py | 218 | Get directory for a specific profile. Args: profile: Profile name. If None, resolves via resolve_profile(). |
| HIGH | src/notebooklm/_chat_notes.py | 181 | Build CREATE_NOTE params for the saved-from-chat variant. Produces the 7-element params array used by the web UI's |
| HIGH | src/notebooklm/_chat_notes.py | 283 | Save a chat answer as a citation-rich note via the saved-from-chat CREATE_NOTE variant (issue #660). Unlike the |
| HIGH | src/notebooklm/_deprecation.py | 157 | Resolve a renamed keyword, warning if the deprecated name was used. Maps a deprecated keyword (``old``) onto its re |
| HIGH | src/notebooklm/_idempotency.py | 84 | Probe-then-retry wrapper for mutating create RPCs. Args: create: Coroutine factory that issues the create R |
| HIGH | src/notebooklm/_chat.py | 302 | Ask the notebook a question. Args: notebook_id: The notebook ID. question: The question |
| HIGH | src/notebooklm/_chat.py | 838 | Save a chat answer as a citation-rich note (issue #660). Unlike :meth:`NotesAPI.create`, this preserves the ``[ |
| HIGH | src/notebooklm/_sources.py | 200 | Wait for a source to become ready. Polls the source status until it becomes READY or ERROR, or timeout. |
| HIGH | src/notebooklm/_sources.py | 253 | Wait for a source to be registered server-side (status >= PROCESSING). Polls until the source is visible in the |
| HIGH | src/notebooklm/_sources.py | 303 | Wait for multiple sources to become ready in parallel. Args: notebook_id: The notebook ID. |
| HIGH | src/notebooklm/_sources.py | 391 | Add a text source (copied text) to a notebook. Args: notebook_id: The notebook ID. titl |
| HIGH | src/notebooklm/_sources.py | 445 | Add a file source to a notebook using resumable upload. Uses Google's resumable upload protocol: 1. Reg |
| HIGH | src/notebooklm/_sources.py | 713 | Get the full content of a source. Args: notebook_id: The notebook ID. source_id: The so |
| HIGH | src/notebooklm/_notebooks.py | 468 | Get notebook details. Args: notebook_id: The notebook ID. Returns: Notebook ob |
| HIGH | src/notebooklm/_artifacts.py | 866 | Wait for a generation task to complete. Uses exponential backoff for polling to reduce API load. Concu |
| HIGH | src/notebooklm/_artifacts.py | 1126 | Select an artifact from candidates by ID or return latest completed. This is the single point where completed-a |
| HIGH | src/notebooklm/utils.py | 37 | Return the surrounding source-text passage for a chat citation. A :class:`~notebooklm.types.ChatReference` carries |
| HIGH | src/notebooklm/_row_adapters_sources.py | 172 | Normalize any of the three source wire shapes into a :class:`SourceRow`. Shapes handled (matching the l |
| HIGH | src/notebooklm/artifacts.py | 74 | Run an artifact-generation callable with rate-limit retry. The callable is always invoked at least once. Retries ha |
| HIGH | src/notebooklm/_auth/cookies.py | 162 | Extract Google cookies from Playwright storage state for NotebookLM auth. Filters cookies to include those from .go |
| HIGH | src/notebooklm/_auth/cookies.py | 258 | Load Playwright storage state from file or environment variable. This is a shared helper used by load_auth_from_sto |
| HIGH | src/notebooklm/_auth/cookies.py | 317 | Load cookies as an httpx.Cookies object for authenticated downloads. Unlike load_auth_from_storage() which returns |
| HIGH | src/notebooklm/_auth/cookies.py | 360 | Extract Google cookies from storage state preserving original identity. Returns a path-aware ``(name, domain, path) |
| HIGH | src/notebooklm/_auth/cookies.py | 396 | Build an httpx.Cookies jar with original domains preserved. This function loads cookies from storage and creates a |
| HIGH | src/notebooklm/_auth/tokens.py | 130 | Create AuthTokens from Playwright storage state file. This is the recommended way to create AuthTokens for prog |
| HIGH | src/notebooklm/_auth/tokens.py | 247 | Load Google cookies from storage as a flat name→value dict. Loads authentication cookies with the following precede |
| HIGH | src/notebooklm/_auth/extraction.py | 64 | Extract a ``WIZ_global_data[key]`` value from a NotebookLM HTML response. NotebookLM (and other Google products) em |
| HIGH | src/notebooklm/_auth/extraction.py | 182 | Extract CSRF token (SNlM0e) from NotebookLM page HTML. The CSRF token is embedded in the page's WIZ_global_dat |
| HIGH | src/notebooklm/_auth/extraction.py | 222 | Extract session ID (FdrFJe) from NotebookLM page HTML. The session ID is embedded in the page's WIZ_global_dat |
| HIGH | src/notebooklm/_auth/refresh.py | 656 | Internal: fetch CSRF and session tokens using a pre-built cookie jar. This is the single implementation for all tok |
| HIGH | src/notebooklm/_auth/refresh.py | 730 | Fetch tokens from a cookie mapping. For backward compatibility. Prefer AuthTokens.from_storage() which preserves co |
| HIGH | src/notebooklm/_auth/refresh.py | 779 | Fetch tokens with domain-preserving cookies from storage. Used by CLI helpers. Loads storage, builds jar, fetches t |
| HIGH | src/notebooklm/_auth/account.py | 144 | Enumerate Google accounts visible to the given cookie jar. Probes ``https://notebooklm.google.com/?authuser=N`` for |
| HIGH | src/notebooklm/cli/resolve.py | 44 | Validate and normalize an entity ID. Args: entity_id: The ID to validate. entity_name: Name for err |
| HIGH | src/notebooklm/cli/resolve.py | 124 | Get notebook ID from argument, env var, or active context. Resolution order (env-var precedence): 1. ``noteboo |
| HIGH | src/notebooklm/cli/resolve.py | 205 | Resolve a partial ID against a **pre-fetched** item list. Sync core of the partial-ID matching logic. Encapsulates |
| HIGH | src/notebooklm/cli/resolve.py | 337 | Resolve a case-insensitive partial ID prefix to a full entity ID. Allows users to type partial IDs like ``abc`` ins |
| HIGH | src/notebooklm/cli/_firefox_containers.py | 228 | Resolve a ``--browser-cookies 'firefox::<spec>'`` to a selector. Args: profile_path: Firefox profile direct |
| HIGH | src/notebooklm/cli/_firefox_containers.py | 409 | Read ``cookies.sqlite`` filtered to a single container. Args: profile_path: Firefox profile directory conta |
| HIGH | src/notebooklm/cli/_chromium_profiles.py | 316 | Read and decrypt cookies from a single Chromium user-data profile. Uses ``rookiepy.any_browser(db_path, domains=…)` |
| HIGH | src/notebooklm/cli/download_helpers.py | 21 | Resolve a partial artifact ID to a full ID. UUID-shaped IDs (canonical 8-4-4-4-12 hex layout, case-insensitive - |
| HIGH | src/notebooklm/cli/download_helpers.py | 94 | Select an artifact from a list based on criteria. CRITICAL: Implements Filter -> Count -> Select logic: 1. |
| HIGH | src/notebooklm/cli/auth_runtime.py | 78 | Get auth components from context. Args: ctx: Click context with optional storage_path in obj Returns: |
| HIGH | src/notebooklm/cli/input.py | 11 | Read all of stdin as UTF-8 text and strip surrounding whitespace. Centralizes the Unix ``-`` stdin convention used |
| HIGH | src/notebooklm/cli/input.py | 42 | Resolve prompt text from a positional argument or ``--prompt-file``. Exactly one source may be provided. The file/s |
| HIGH | src/notebooklm/cli/services/research.py | 104 | Resolve, wait for completion, and optionally import. Args: plan: User inputs validated by the Click handler |
| HIGH | src/notebooklm/cli/services/generate.py | 293 | Validate Click-layer inputs and return a :class:`GenerationPlan`. Args: kind: One of the literal kind names |
| HIGH | src/notebooklm/cli/services/download.py | 237 | Validate + assemble a :class:`DownloadPlan` from raw Click args. Synchronous: rejects flag conflicts with a typed v |
| HIGH | src/notebooklm/cli/services/login/cookie_jar.py | 79 | Probe ``?authuser=N`` against one cookie set and return tagged Accounts. Shared by both the legacy single-jar path |
| HIGH | src/notebooklm/cli/services/login/firefox_accounts.py | 38 | Load Google cookies from a specific Firefox Multi-Account Container. Bypasses rookiepy because rookiepy 0.5.6 does |
| HIGH | src/notebooklm/cli/services/login/profile_targets.py | 41 | Derive a valid profile name from an email address. Profile names are restricted to ``[a-zA-Z0-9_-]`` (see :data |
| HIGH | src/notebooklm/rpc/decoder.py | 220 | Parse chunked response format (rt=c mode). Format is alternating lines of: - byte_count (integer) - js |
| HIGH | src/notebooklm/rpc/decoder.py | 649 | Complete decode pipeline: strip prefix -> parse chunks -> extract result. Args: raw_response: Raw resp |
| HIGH | src/notebooklm/rpc/_safe_index.py | 60 | Walk ``data`` by ``path`` indices with soft-strict drift handling. Args: data: Nested list/tuple structure |
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | tests/vcr_config.py | 467 | 1. **Streaming-chat envelope** ``[null, "<inner_json>"]`` where the inner |
| HIGH | tests/vcr_config.py | 473 | 2. **Batchexecute envelope** ``[[[rpc_id, args_json, null, "generic"]]]`` |
| HIGH | tests/vcr_config.py | 542 | # Streaming-chat envelope: [null, "<inner_json>"]. |
| HIGH | tests/unit/test_vcr_config.py | 87 | JSON envelope is ``[null, "<inner_json>"]`` and ``<inner_json>`` is itself a |
| HIGH | tests/unit/test_decoder.py | 867 | """[5, null, 'x'] is not the bare form — no enrichment.""" |
| HIGH | tests/unit/test_chat_ask_invariants.py | 294 | '63\n[["wrb.fr","hPTbtc","[[[\\"real-conv-id-from-hptbtc\\"]]]",null,null]]' |
| HIGH | tests/unit/test_cassette_shapes.py | 448 | f"[null, '<inner-json>'], got {type(freq).__name__} {freq!r:.120}" |
| HIGH | tests/unit/test_cassette_shapes.py | 29 | the new 9-param outer shape ``[null, "<inner-json-string>"]`` whose inner |
| HIGH | tests/unit/test_cassette_shapes.py | 440 | Real chat-ask `f.req` is ``[null, "<inner-json>"]`` whose inner JSON |
| HIGH | tests/unit/test_api_coverage.py | 133 | # Real API returns 3 levels of nesting: [[[null, [summary], [[keywords]], []]]] |
| HIGH | tests/unit/test_api_coverage.py | 180 | # Response format: [[[title, description, null, null, prompt, audience_level], ...]] |
| HIGH | tests/unit/test_api_coverage.py | 301 | # Verify reference payload: [null, ["source_id"], [2]] |
| HIGH | tests/unit/test_api_coverage.py | 314 | # Verify reference payload: [null, ["source_id"], [2]] |
| HIGH | tests/unit/test_authed_post_pipeline.py | 1488 | decoded_payload = b')]}\'\n\n[["wrb.fr",null,"[1]",null,null,null,"generic"]]' |
| HIGH | tests/unit/test_select_artifact.py | 276 | ever emits ``[null, ...]`` at index 15 the sort must coerce that |
| HIGH | tests/unit/test_auth_cookie_save_race.py | 1007 | f"None-valued cookie must never be persisted as value:null, got: {stored}" |
| HIGH | tests/unit/test_save_chat_as_note_encoder.py | 105 | # passage[3] = [[null, start, end]]; passage[5] = [[[passage_id], source_id]]; |
| HIGH | tests/unit/test_save_chat_as_note_encoder.py | 201 | assert "[0,0,0,null,null,null,null,0,0]" in actual_json |
| HIGH | tests/unit/test_rate_limit_retry.py | 46 | resp.text = ")]}'\n[null,[" + str(payload).replace("'", '"') + "]]" |
| HIGH | tests/unit/test_rescrub_cassettes_script.py | 95 | f'[["wrb.fr","JFMDGd","[[null,[\\"{avatar_url}\\"]]]",null,null,null,"generic"]]' |
| HIGH | tests/unit/test_rpc_overrides.py | 171 | '{"LIST_NOTEBOOKS": null, "CREATE_NOTEBOOK": "valid"}', |
| HIGH | tests/unit/test_vcr_body_matcher.py | 89 | """Build a streaming-chat ``f.req`` form body (`[null, "<inner_json>"]`).""" |
| HIGH | tests/unit/test_vcr_body_matcher.py | 180 | fixtures: the cassette has a trailing ``[null, null]`` while the live |
| HIGH | tests/unit/test_vcr_body_matcher.py | 78 | is ``[[[rpc_id, "<args_json>", null, "generic"]]]`` and ``<args_json>`` |
| HIGH | tests/unit/test_vcr_body_matcher.py | 310 | The outer ``[[[rpc, "...", null, "generic"]]]`` shape parses cleanly, |
| HIGH | tests/unit/test_source_selection.py | 1052 | # Response format: [[[title, description, null, null, prompt, audience_level], ...]] |
| HIGH | tests/integration/test_vcr_comprehensive.py | 791 | # Drive sources return [[null, true, [source_id]]] when fresh |
| HIGH | tests/integration/test_artifacts_integration.py | 2321 | """An empty (non-null) CREATE_ARTIFACT result drifts and raises. |
| HIGH | tests/integration/test_chat_delete_conversation_vcr.py | 192 | assert params[2] is None, f"slot 2 must be null, got {params[2]!r}" |
| HIGH | tests/integration/test_vcr_example.py | 90 | 'f.req=[[["methodId",null,null,[[["notebook_id","data"]]]]]]&at=fake_csrf_token' |
| HIGH | tests/integration/test_notebooks_integration.py | 301 | # First response for rename (returns null) |
| HIGH | tests/integration/test_notebooks_integration.py | 334 | # Rename response (returns null) |
| HIGH | tests/integration/test_notebooks_integration.py | 374 | None, # Share returns null, we build the URL |
| HIGH | tests/integration/test_sources_integration.py | 610 | assert is_fresh is True, "Nested [null, true, ...] should mean source is fresh" |
| HIGH | tests/integration/test_sources_integration.py | 636 | # Real API returns 3 levels of nesting: [[[null, [summary], [[keywords]], []]]] |
| HIGH | tests/integration/test_sources_integration.py | 600 | Drive sources return [[null, true, [source_id]]] when fresh, |
| HIGH | tests/integration/concurrency/test_chat_history_race.py | 80 | ``[null, "<params-json>"]``; the inner params list matches the order |
| HIGH | scripts/check_rpc_health.py | 453 | # Params structure: [[[null,[[null,null,null,null,["language_code"]]]]]] |
| HIGH | src/notebooklm/_sources.py | 667 | # - [[null, true, [source_id]]]: source is fresh (Drive sources) |
| HIGH | src/notebooklm/_sources.py | 678 | # Check for nested structure [[null, true, ...]] from Drive sources |
| HIGH | src/notebooklm/_settings.py | 176 | # Params structure: [[[null,[[null,null,null,null,["language_code"]]]]]] |
| HIGH | src/notebooklm/_notebooks.py | 537 | # [notebook_id, [[null, null, null, [null, new_title]]]] |
| HIGH | src/notebooklm/cli/services/playwright_login.py | 122 | " 2. Or run: notebooklm auth logout && notebooklm login" |
| HIGH | src/notebooklm/rpc/decoder.py | 639 | # we never downgrade a non-null result back to null). |
| HIGH | src/notebooklm/rpc/decoder.py | 716 | # null, so raise regardless of ``allow_null``. |
| HIGH | src/notebooklm/rpc/overrides.py | 62 | # ``json.loads('{"X": null}')`` would coerce to ``str(None) == |
| HIGH | src/notebooklm/rpc/encoder.py | 22 | [[[rpc_id, json_params, null, "generic"]]] |
| HIGH | src/notebooklm/rpc/encoder.py | 42 | # Build inner request: [rpc_id, json_params, null, "generic"] |
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | README.md | 121 | # 1. Authenticate (opens browser) |
| LOW | tests/cassette_patterns.py | 101 | |
| LOW | tests/cassette_patterns.py | 321 | "SCRUBBED_DRIVE_FILE_ID", |
| LOW | tests/cassette_patterns.py | 381 | # theory contain regex metacharacters (none do today, but future additions |
| LOW | tests/cassette_patterns.py | 401 | # |
| LOW | tests/cassette_patterns.py | 441 | # storage_state JSON — and ``is_clean`` (whose detector IS registry-derived via |
| LOW | tests/cassette_patterns.py | 461 | # These run FIRST, before any cookie-name-anchored pattern, so a session |
| LOW | tests/cassette_patterns.py | 481 | # ------------------------------------------------------------------------- |
| LOW | tests/cassette_patterns.py | 521 | # JSON-quoted form. The replacement embeds ``@example.com`` so a second |
| LOW | tests/cassette_patterns.py | 541 | # here: that would clobber legitimate two-Capitalized-word fixture content |
| LOW | tests/cassette_patterns.py | 601 | # full upload URL above). |
| LOW | tests/cassette_patterns.py | 621 | ( |
| LOW | tests/cassette_patterns.py | 641 | # quote inside a JSON string) so it never fires on bare ``"Foo Bar"`` |
| LOW | tests/cassette_patterns.py | 741 | # Each entry is (label, regex) where the regex's group(1) captures the value |
| LOW | tests/cassette_patterns.py | 761 | ] |
| LOW | tests/cassette_patterns.py | 881 | # --- 6. Escaped display-name literals ---------------------------------- |
| LOW | tests/cassette_patterns.py | 901 | # definition — this is the cookie-name-agnostic backstop that closes the |
| LOW | tests/vcr_config.py | 641 | # Catches the regression class P1-3 targets — different arg counts, |
| LOW | tests/vcr_config.py | 661 | # Set NOTEBOOKLM_VCR_RECORD=1 (or =true, =yes) to record new cassettes |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 61 | # so any stray reference raises ``AttributeError`` at access time — |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 81 | "_reqid_counter", |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 101 | |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 521 | violations.append( |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 61 | from pathlib import Path |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 101 | # at a word boundary, so we match the full chain regardless of how deep |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 141 | _ALLOWLIST: frozenset[str] = frozenset( |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 161 | # side-effects/idempotency cassettes); these patches sit below the |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 181 | # `_try_claim_rotation`, `_file_lock_try_exclusive`, |
| LOW | tests/_lint/test_no_forbidden_monkeypatches.py | 201 | # database-discovery helpers — CLI-side seam outside `make_fake_core`. |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 81 | # Each entry is a primitive whose construction site is loop-safe by an |
| LOW | tests/unit/test_chat_characterization.py | 2081 | 1, |
| LOW | tests/unit/test_vcr_config.py | 361 | assert "real_sid_value" not in out |
| LOW | tests/unit/conftest.py | 41 | """ |
| LOW | tests/unit/conftest.py | 61 | # wrapper's ``aiter_bytes`` + rebuild path works on it. Returning a |
| LOW | tests/unit/test_cassette_sanitizer.py | 481 | entries = { |
| LOW | tests/unit/test_cassette_sanitizer.py | 501 | # JSON payload with only sensitive scalars scrubbed inside). |
| LOW | tests/unit/test_cassette_shapes.py | 81 | # The "67 cassettes with /ogw/" dynamic set that used to surface here was |
| LOW | tests/unit/test_cassette_shapes.py | 561 | # Encoding-header coverage (issue #773 follow-up to #769 / #771) |
| LOW | tests/unit/test_authed_post_pipeline.py | 481 | ) |
| LOW | tests/unit/test_auth_cookie_save_race.py | 1701 | # Two jars representing distinct post-rotation states. The save that |
| LOW | tests/unit/test_atomic_update_json.py | 321 | # Lock-path derivation contract (#1220) |
| LOW | tests/unit/test_init_order.py | 321 | # Artifact-service "reach-in" guard |
| LOW | tests/unit/test_research_api.py | 701 | ): |
| LOW | tests/unit/test_public_shims.py | 201 | |
| LOW | tests/unit/test_public_shims.py | 821 | assert frozenset().union(*OPTIONAL_COOKIE_DOMAINS_BY_LABEL.values()) == OPTIONAL_COOKIE_DOMAINS |
| LOW | tests/unit/test_public_shims.py | 1141 | method=RPCMethod.LIST_NOTEBOOKS, |
| LOW | tests/unit/test_public_shims.py | 1241 | |
| LOW | tests/unit/test_notes_unit.py | 681 | assert params[1] is None |
| LOW | tests/unit/test_refresh_cmd_redaction.py | 261 | # The full exception (with its cause chain) is what DEBUG-level captures |
| LOW | tests/unit/test_rpc_health_coverage.py | 61 | # --------------------------------------------------------------------------- |
| LOW | tests/unit/test_rpc_health_coverage.py | 81 | "CREATE_ARTIFACT", |
| LOW | tests/unit/test_runtime_lifecycle.py | 121 | # needs an async implementation. |
| LOW | tests/unit/test_runtime_lifecycle.py | 141 | # so a client reopened on a different loop rebuilds the semaphore on |
| LOW | tests/unit/test_runtime_lifecycle.py | 281 | host._drain_tracker.reset_after_open.assert_called_once_with() |
| LOW | tests/unit/test_runtime_lifecycle.py | 701 | # |
| LOW | tests/unit/test_chat_transport.py | 341 | # which exercises a real ``TransportDrainTracker`` end-to-end rather |
| LOW | tests/unit/test_cassette_patterns.py | 641 | |
| LOW | tests/unit/test_vcr_body_matcher.py | 101 | # is the spec's "widen the volatile-key scrub list" escape hatch applied at |
| LOW | tests/unit/test_source_selection.py | 121 | # SimpleNamespace constructor satisfies the policy by setting every |
| LOW | tests/unit/cli/test_download.py | 2061 | # `helpers.handle_error()` shim that always exits 1 with a text-only message |
| 168 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | tests/unit/test_composition_primitives.py | 0 | sentinel callable — identity-checked, never invoked. |
| HIGH | tests/unit/test_composition_primitives.py | 0 | sentinel callable — identity-checked, never invoked. |
| HIGH | tests/unit/test_composition_primitives.py | 0 | sentinel callable — identity-checked, never invoked. |
| HIGH | tests/unit/test_composition_primitives.py | 0 | sentinel callable — identity-checked, never invoked. |
| HIGH | tests/unit/test_row_adapters.py | 0 | the adapter is frozen so the wrapped row can't be swapped out. |
| HIGH | tests/unit/test_row_adapters.py | 0 | the adapter is frozen so the wrapped row can't be swapped out. |
| HIGH | tests/unit/test_row_adapters.py | 0 | the adapter is frozen so the wrapped row can't be swapped out. |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. |
| HIGH | tests/unit/cli/test_source.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. |
| HIGH | tests/unit/cli/test_note.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path a under ``--json``: typed json error doc + exit 1. |
| HIGH | tests/unit/cli/test_source.py | 0 | path a under ``--json``: typed json error doc + exit 1. |
| HIGH | tests/unit/cli/test_note.py | 0 | path a under ``--json``: typed json error doc + exit 1. |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. |
| HIGH | tests/unit/cli/test_source.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. |
| HIGH | tests/unit/cli/test_note.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. |
| HIGH | …ts/integration/test_artifact_generation_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | tests/integration/test_research_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | tests/integration/test_side_effects_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | tests/integration/test_notes_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | …sts/integration/concurrency/test_idempotency_create.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | …sts/integration/concurrency/test_note_create_cancel.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. |
| HIGH | src/notebooklm/_types/research.py | 0 | return the historical compatibility dictionary shape. |
| HIGH | src/notebooklm/_types/research.py | 0 | return the historical compatibility dictionary shape. |
| HIGH | src/notebooklm/_types/research.py | 0 | return the historical compatibility dictionary shape. |
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/unit/test_windows_compatibility.py | 316 | except Exception as e: |
| LOW | tests/unit/concurrency/test_close_cancellation_leak.py | 369 | except Exception: |
| LOW | tests/integration/test_chat_delete_conversation_vcr.py | 144 | except Exception as exc: # noqa: BLE001 |
| LOW | tests/integration/test_chat_multi_source_vcr.py | 277 | except Exception as exc: # noqa: BLE001 — best-effort cleanup |
| LOW | tests/integration/concurrency/test_add_file_toctou.py | 235 | except Exception: # noqa: BLE001 — defensive |
| LOW | tests/scripts/setup-generation-notebook.py | 74 | except Exception as exc: # noqa: BLE001 — top-level CLI surface |
| MEDIUM | tests/e2e/conftest.py | 47 | def _ask_with_skip(*args, **kwargs): |
| LOW | tests/e2e/conftest.py | 407 | except Exception as e: |
| LOW | tests/e2e/conftest.py | 468 | except Exception: |
| LOW | tests/e2e/conftest.py | 583 | except Exception: |
| LOW | tests/e2e/conftest.py | 592 | except Exception: |
| LOW | tests/e2e/conftest.py | 661 | except Exception as e: |
| LOW | tests/e2e/conftest.py | 687 | except Exception: |
| LOW | tests/e2e/conftest.py | 705 | except Exception: |
| LOW | tests/e2e/conftest.py | 548 | except Exception: |
| LOW | tests/e2e/conftest.py | 550 | except Exception: |
| LOW | tests/e2e/conftest.py | 561 | except Exception: |
| LOW | tests/e2e/conftest.py | 563 | except Exception: |
| LOW | tests/e2e/conftest.py | 795 | except Exception: |
| LOW | tests/e2e/conftest.py | 797 | except Exception: |
| LOW | tests/e2e/conftest.py | 857 | except Exception as e: |
| LOW | tests/e2e/test_downloads.py | 132 | except Exception: |
| LOW | docs/rpc-reference.md | 117 | except Exception: |
| LOW | docs/examples/notes.py | 157 | except Exception as e: |
| LOW | docs/examples/bulk-import.py | 64 | except Exception as e: |
| LOW | docs/examples/bulk-import.py | 75 | except Exception as e: |
| LOW | docs/examples/bulk-import.py | 86 | except Exception as e: |
| LOW | docs/examples/chat.py | 102 | except Exception as e: |
| LOW | scripts/diagnose_get_notebook.py | 130 | except Exception as e: |
| LOW | scripts/audit_public_api_compat.py | 317 | except Exception as exc: |
| LOW | scripts/check_rpc_health.py | 912 | except Exception as e: |
| LOW | scripts/check_rpc_health.py | 928 | except Exception as e: |
| LOW | scripts/check_rpc_health.py | 944 | except Exception as e: |
| LOW | scripts/check_rpc_health.py | 953 | except Exception as e: |
| MEDIUM | src/notebooklm/_source_upload.py | 809 | def _probe() -> str | None: |
| LOW | src/notebooklm/_source_upload.py | 181 | except Exception: # noqa: BLE001 - hint lookup must not mask the upload error. |
| LOW | src/notebooklm/_source_upload.py | 801 | except Exception: |
| LOW | src/notebooklm/_source_upload.py | 817 | except Exception: |
| LOW | src/notebooklm/_source_upload.py | 1138 | except Exception as close_exc: # noqa: BLE001 |
| LOW | src/notebooklm/_source_upload.py | 1164 | except Exception as exc: # noqa: BLE001 |
| LOW | src/notebooklm/_source_upload.py | 1174 | except Exception as close_exc: # noqa: BLE001 |
| LOW | src/notebooklm/_source_upload.py | 1202 | except Exception as exc: # noqa: BLE001 |
| LOW | src/notebooklm/_auth_refresh_retry.py | 139 | except Exception as refresh_error: |
| LOW | src/notebooklm/_chat_wire.py | 441 | except Exception: |
| LOW | src/notebooklm/client.py | 663 | except Exception as close_exc: |
| LOW | src/notebooklm/_runtime_lifecycle.py | 507 | except Exception as e: |
| LOW | src/notebooklm/_runtime_lifecycle.py | 583 | except Exception as exc: # noqa: BLE001 - opportunistic best-effort |
| LOW | src/notebooklm/_runtime_lifecycle.py | 595 | except Exception as exc: # noqa: BLE001 |
| LOW | src/notebooklm/_notebooks.py | 334 | except Exception: |
| LOW | src/notebooklm/_notebooks.py | 384 | except Exception: |
| LOW | src/notebooklm/_notebooks.py | 424 | except Exception: |
| LOW | src/notebooklm/_notebooks.py | 438 | except Exception: |
| MEDIUM | src/notebooklm/_notebooks.py | 341 | def _create() -> Notebook: |
| LOW | src/notebooklm/_source_add.py | 94 | except Exception: |
| LOW | src/notebooklm/_source_add.py | 258 | except Exception: |
| MEDIUM | src/notebooklm/_source_add.py | 84 | def _probe() -> Source | None: |
| MEDIUM | src/notebooklm/_source_add.py | 251 | def _probe() -> Source | None: |
| LOW | src/notebooklm/_middleware_metrics.py | 112 | except Exception as exc: |
| LOW | src/notebooklm/_middleware_tracing.py | 115 | except Exception as exc: |
| LOW | src/notebooklm/_client_metrics.py | 146 | except Exception as exc: # noqa: BLE001 - observability must not alter behavior |
| 34 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/vcr_config.py | 460 | |
| LOW | tests/vcr_config.py | 512 | |
| LOW | tests/_lint/test_no_session.py | 60 | |
| LOW | tests/_lint/test_no_session_compat_bridges.py | 165 | |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 162 | |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 259 | |
| LOW | tests/_lint/test_session_runtime_boundaries.py | 329 | |
| LOW | tests/_lint/test_client_composition.py | 77 | |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 73 | |
| LOW | tests/_lint/test_no_legacy_rpc_callable_aliases.py | 95 | |
| LOW | tests/_lint/test_no_core_imports.py | 73 | |
| LOW | tests/_lint/test_asyncio_loop_affinity_guard.py | 234 | |
| LOW | tests/unit/test_middleware_context_contract.py | 92 | |
| LOW | tests/unit/test_streaming_chat_wire.py | 543 | |
| LOW | tests/unit/test_concurrency_refresh_race.py | 425 | |
| LOW | tests/unit/test_cli_boundary.py | 178 | |
| LOW | tests/unit/test_cli_boundary.py | 209 | |
| LOW | tests/unit/test_cli_boundary.py | 242 | |
| LOW | tests/unit/test_cli_boundary.py | 343 | |
| LOW | tests/unit/test_cli_boundary.py | 430 | |
| LOW | tests/unit/test_cli_boundary.py | 541 | |
| LOW | tests/unit/test_public_surface.py | 188 | |
| LOW | tests/unit/test_type_boundaries.py | 59 | |
| LOW | tests/unit/test_type_boundaries.py | 134 | |
| LOW | tests/unit/test_type_boundaries.py | 162 | |
| LOW | tests/unit/test_type_boundaries.py | 185 | |
| LOW | tests/unit/test_init_order.py | 900 | |
| LOW | tests/unit/test_rpc_types.py | 22 | |
| LOW | tests/unit/test_public_shims.py | 579 | |
| LOW | tests/unit/test_install_docs.py | 88 | |
| LOW | tests/unit/test_install_docs.py | 215 | |
| LOW | tests/unit/test_rpc_overrides.py | 59 | |
| LOW | tests/unit/cli/test_quiet_enforcement.py | 109 | |
| LOW | tests/integration/test_sources_integration.py | 1711 | |
| LOW | tests/integration/concurrency/helpers.py | 55 | |
| LOW | tests/scripts/check_cassettes_clean.py | 136 | |
| LOW | tests/scripts/compress_polling_cassette.py | 120 | |
| LOW | tests/e2e/conftest.py | 264 | |
| LOW | tests/e2e/conftest.py | 537 | |
| LOW | docs/examples/notes.py | 23 | |
| LOW | scripts/diagnose_get_notebook.py | 136 | |
| LOW | scripts/check_deprecation_targets.py | 166 | |
| LOW | scripts/audit_test_suite.py | 99 | |
| LOW | scripts/audit_test_suite.py | 114 | |
| LOW | scripts/audit_test_suite.py | 171 | |
| LOW | scripts/audit_public_api_compat.py | 329 | |
| LOW | scripts/audit_public_api_compat.py | 395 | |
| LOW | scripts/check_workflow_secret_gates.py | 227 | |
| LOW | scripts/check_ci_install_parity.py | 55 | |
| LOW | scripts/check_rpc_health.py | 668 | |
| LOW | src/notebooklm/_research.py | 590 | |
| LOW | src/notebooklm/_research.py | 710 | |
| LOW | src/notebooklm/_source_upload.py | 1064 | |
| LOW | src/notebooklm/_source_upload.py | 1102 | |
| LOW | src/notebooklm/_research_task_parser.py | 172 | |
| LOW | src/notebooklm/paths.py | 361 | |
| LOW | src/notebooklm/_chat_wire.py | 264 | |
| LOW | src/notebooklm/_chat_wire.py | 427 | |
| LOW | src/notebooklm/_chat_wire.py | 608 | |
| LOW | src/notebooklm/_chat.py | 562 | |
| 44 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/unit/test_cookie_domain_split.py | 348 | # Step 2: storage_state → DomainCookieMap (used by AuthTokens). |
| LOW | tests/unit/test_cookie_domain_split.py | 354 | # Step 3: storage_state → httpx jar (used by downloads + refresh). |
| LOW | tests/unit/test_cookie_domain_split.py | 362 | # Step 4: the runtime gate itself accepts every YouTube variant. |
| LOW | tests/unit/test_cookie_domain_split.py | 337 | # Step 1: rookiepy → storage_state conversion must keep the YouTube |
| LOW | tests/integration/test_sources_integration.py | 714 | # Step 0: register_file_source captures a baseline GET_NOTEBOOK |
| LOW | tests/integration/test_sources_integration.py | 717 | # Step 1: Mock RPC registration response (o4cbdc) |
| LOW | tests/integration/test_sources_integration.py | 727 | # Step 2: Mock upload session start response |
| LOW | tests/integration/test_sources_integration.py | 737 | # Step 3: Mock upload finalize response |
| LOW | tests/integration/test_sources_integration.py | 931 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. |
| LOW | tests/integration/test_sources_integration.py | 933 | # Step 1: Mock RPC registration |
| LOW | tests/integration/test_sources_integration.py | 943 | # Step 2: Mock upload session start |
| LOW | tests/integration/test_sources_integration.py | 953 | # Step 3: Mock upload finalize |
| LOW | tests/integration/test_sources_integration.py | 2680 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. |
| LOW | tests/integration/test_sources_integration.py | 2682 | # Step 1: Successful RPC registration |
| LOW | tests/integration/test_sources_integration.py | 2692 | # Step 2: Upload session response WITHOUT the required upload URL header |
| LOW | tests/e2e/test_research_import_verification.py | 42 | # Step 1: Start fast web research |
| LOW | tests/e2e/test_research_import_verification.py | 56 | # Step 2: Poll until complete |
| LOW | tests/e2e/test_research_import_verification.py | 74 | # Step 3: Get sources to import |
| LOW | tests/e2e/test_research_import_verification.py | 88 | # Step 4: Import sources. Use the verification-aware variant so an |
| LOW | tests/e2e/test_research_import_verification.py | 99 | # Step 5: Poll for imported sources to appear |
| LOW | tests/e2e/test_research_import_verification.py | 111 | # Step 6: Verify source count |
| LOW | tests/e2e/test_research.py | 199 | # Step 1: Start research |
| LOW | tests/e2e/test_research.py | 210 | # Step 2: Poll until complete |
| LOW | tests/e2e/test_research.py | 232 | # Step 3: Import sources (if any found) |
| LOW | docs/examples/video.py | 26 | # Step 1: Create a notebook with content |
| LOW | docs/examples/video.py | 45 | # Step 2: Generate the video overview |
| LOW | docs/examples/video.py | 78 | # Step 3: Wait for completion with status updates |
| LOW | docs/examples/video.py | 93 | # Step 4: Download the video |
| LOW | src/notebooklm/cli/download_helpers.py | 122 | # Step 1: Filter |
| LOW | src/notebooklm/cli/download_helpers.py | 140 | # Step 2: Count |
| LOW | src/notebooklm/cli/download_helpers.py | 143 | # Step 3: Select |
| Severity | File | Line | Snippet |
|---|---|---|---|
| MEDIUM | tests/cassette_patterns.py | 181 | # fixed number of characters) is robust to alternate-length prefixes if |
| MEDIUM | tests/vcr_config.py | 318 | # matcher robust across recording sessions while still failing on meaningful |
| MEDIUM | tests/vcr_config.py | 643 | # or extra non-volatile dict keys — while staying robust against leaf |
| MEDIUM | tests/unit/test_json_stdout_purity.py | 361 | # so the mock-client harness can't drive them. The parametrized sweep |
| MEDIUM | tests/unit/test_json_stdout_purity.py | 509 | # the mock-client harness. `notebook_create` goes through the standard |
| MEDIUM | tests/unit/test_json_stdout_purity.py | 534 | # harness — they read NOTEBOOKLM_HOME directly. Stage a clean layout |
| MEDIUM | tests/unit/test_auth_cookie_save_race.py | 1716 | # order) to stay robust across schedulers. |
| MEDIUM | tests/unit/test_json_error_exit.py | 613 | # then invoke without the mock-client harness (these commands don't |
| MEDIUM | tests/unit/test_json_error_exit.py | 588 | # Filesystem-driven failure cases bypass the mock-client harness — they |
| MEDIUM | tests/unit/test_vcr_body_matcher.py | 209 | # Volatile-key stripping — keep matching robust across recording timestamps |
| MEDIUM | tests/integration/concurrency/test_harness_smoke.py | 50 | # concurrency-harness smoke tests against a mock transport; no |
| MEDIUM | tests/integration/concurrency/test_harness_smoke.py | 138 | # Peak in-flight was high — the harness is genuinely fanning out. |
| MEDIUM | …ts/integration/concurrency/test_max_concurrent_rpcs.py | 71 | # concurrency-harness tests against a mock transport; no HTTP, |
| MEDIUM | …ts/integration/concurrency/test_max_concurrent_rpcs.py | 155 | # Anything <= 8 would mean the gate isn't kicking in (the harness |
| MEDIUM | tests/e2e/test_research_import_verification.py | 92 | # makes this path robust for fast-mode imports too. |
| LOW | src/notebooklm/_sharing.py | 128 | # Fetch current status and override view_level with what we just set |
| MEDIUM | src/notebooklm/_atomic_io.py | 229 | # and re-introduce the divergent-lock race. ``casefold`` is the robust |
| Severity | File | Line | Snippet |
|---|---|---|---|
| MEDIUM | tests/unit/test_artifact_downloads.py | 764 | # Create the complex nested structure for data table |
| MEDIUM | tests/unit/test_sources_upload.py | 922 | # Create a temp file |
| MEDIUM | tests/unit/cli/test_resolve.py | 333 | # Create a notebook with a short ID that we'll match exactly |
| MEDIUM | tests/unit/cli/test_source.py | 306 | # Create a temp file |
| MEDIUM | tests/unit/cli/test_skill.py | 757 | # Create a sibling file to make skills/ non-empty after notebooklm/ is gone |
| MEDIUM | tests/unit/cli/test_language.py | 230 | # Create the file so exists() returns True, then mock read_text to raise OSError |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 748 | # Create a test file |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 838 | # Create a source to delete |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 876 | # Create a notebook to delete |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 908 | # Create a note to delete |
| MEDIUM | tests/e2e/test_artifacts.py | 248 | # Create a quiz artifact for deletion (different type than flashcards) |
| MEDIUM | tests/e2e/test_sources.py | 123 | # Create a source to delete |
| MEDIUM | docs/examples/notes.py | 49 | # Create a new note with title and content |
| MEDIUM | docs/examples/notes.py | 27 | # Create a notebook for our examples |
| MEDIUM | docs/examples/chat.py | 23 | # Create a notebook with some content |
| MEDIUM | src/notebooklm/notebooklm_cli.py | 167 | notebooklm create "My Notes" # Create a notebook |
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/unit/test_chat_ask_invariants.py | 86 | account_email="user@example.com", |
| LOW | tests/unit/test_chat_ask_invariants.py | 107 | assert _extract_query_param(str(request.url), "authuser") == "user@example.com" |
| LOW | tests/unit/test_token_regex.py | 47 | assert value == "user@example.com" |
| LOW | tests/unit/test_sharing_types.py | 14 | data = ["user@example.com", 3, [], ["Test User", "https://avatar.url"]] |
| LOW | tests/unit/test_sharing_types.py | 17 | assert user.email == "user@example.com" |
| LOW | tests/unit/test_sharing_types.py | 41 | data = ["user@example.com", 2, []] |
| LOW | tests/unit/test_sharing_types.py | 44 | assert user.email == "user@example.com" |
| LOW | tests/unit/test_sharing_types.py | 51 | data = ["user@example.com", 99, []] |
| LOW | tests/unit/test_sharing_types.py | 58 | data = ["user@example.com", {"permission": 3}, []] |
| LOW | tests/unit/test_sharing_types.py | 73 | data = ["user@example.com", 3, [], ["Just Name"]] |
| LOW | tests/unit/cli/test_share.py | 163 | result = runner.invoke(cli, ["share", "add", "user@example.com", "-n", "nb_123"]) |
| LOW | tests/unit/cli/test_share.py | 170 | "user@example.com", |
| LOW | tests/unit/cli/test_share.py | 193 | ["share", "add", "user@example.com", "-n", "nb_123", "-p", "editor"], |
| LOW | tests/unit/cli/test_share.py | 200 | "user@example.com", |
| LOW | tests/unit/cli/test_share.py | 229 | cli, ["share", "remove", "user@example.com", "-n", "nb_123", "-y"] |
| LOW | tests/unit/cli/test_share.py | 234 | mock_client.sharing.remove_user.assert_called_once_with("nb_123", "user@example.com") |
| LOW | tests/unit/cli/test_share.py | 275 | cli, ["share", "remove", "user@example.com", "-n", "nb_123", "--json"] |
| LOW | tests/unit/cli/test_share.py | 279 | assert '"removed_user": "user@example.com"' in result.output |
| LOW | tests/unit/cli/test_share.py | 300 | cli, ["share", "remove", "user@example.com", "-n", "nb_123", "--json"] |
| LOW | tests/unit/cli/test_share.py | 304 | assert '"removed_user": "user@example.com"' in result.output |
| LOW | tests/unit/cli/test_share.py | 306 | mock_client.sharing.remove_user.assert_called_once_with("nb_123", "user@example.com") |
| LOW | tests/unit/cli/test_share.py | 464 | "user@example.com", |
| LOW | tests/unit/cli/test_share.py | 474 | assert '"updated_user": "user@example.com"' in result.output |
| LOW | tests/unit/cli/test_share.py | 546 | cli, ["share", "add", "user@example.com", "-n", "nb_123", "--json"] |
| LOW | tests/unit/cli/test_share.py | 550 | assert '"added_user": "user@example.com"' in result.output |
| LOW | tests/unit/cli/test_share.py | 252 | cli, ["share", "remove", "user@example.com", "-n", "nb_123"], input="n\n" |
| LOW | tests/unit/cli/test_share.py | 434 | ["share", "update", "user@example.com", "-n", "nb_123", "-p", "editor"], |
| LOW | tests/unit/cli/test_share.py | 441 | "nb_123", "user@example.com", SharePermission.EDITOR |
| LOW | tests/unit/cli/test_session_characterization.py | 173 | result = char_runner.invoke(cli, ["login", "--account", "user@example.com"]) |
| LOW | tests/integration/test_sharing_integration.py | 343 | ["user@example.com", 2, [], ["User", "https://avatar"]], # Now editor |
| LOW | tests/integration/test_sharing_integration.py | 354 | "user@example.com", |
| LOW | docs/rpc-reference.md | 1330 | await client.sharing.add_user(notebook_id, "user@example.com", SharePermission.VIEWER) |
| LOW | src/notebooklm/_sharing.py | 30 | "user@example.com", |
| Severity | File | Line | Snippet |
|---|---|---|---|
| CRITICAL | tests/unit/cli/test_generate.py | 976 | assert not mock_client.mind_maps.generate.await_args.kwargs.get("instructions") |
| CRITICAL | tests/unit/cli/test_resolver_characterization.py | 405 | passed_conv_id = mock_client.chat.ask.await_args.kwargs.get("conversation_id") |
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | tests/unit/test_windows_compatibility.py | 265 | # Check if UTF-8 mode is active (either via flag or env var) |
| LOW | tests/unit/test_auth_storage.py | 137 | # Set NOTEBOOKLM_HOME to tmp_path and create a file there |
| LOW | tests/e2e/test_chat.py | 67 | # Check if any references have cited_text |
| LOW | scripts/check_rpc_health.py | 639 | # Check if error response still contains our expected ID |
| LOW | scripts/check_rpc_health.py | 656 | # Check if expected ID is in response |
| LOW | scripts/check_rpc_health.py | 866 | # Check if status indicates completion (status code 3 = ready) |
| LOW | .github/workflows/nightly.yml | 54 | # Check if it's main or a release branch |
| LOW | src/notebooklm/_auth/refresh.py | 702 | # Check if we were redirected to login |
| LOW | src/notebooklm/_auth/cookie_policy.py | 527 | # Check if it's a valid Google domain (base or regional) |
| LOW | src/notebooklm/_auth/cookie_policy.py | 541 | # Check if domain is a subdomain of allowed suffixes |
| LOW | src/notebooklm/cli/chat_cmd.py | 47 | # Check if user switched notebooks via --notebook flag |
| LOW | src/notebooklm/cli/artifact_cmd.py | 305 | # Check if this is a mind map (stored with notes) |
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | docs/python-api.md | 922 | # Get AI-generated description (parsed with suggested topics) |
| HIGH | docs/python-api.md | 1014 | # Get AI-generated summary and keywords (returns a typed SourceGuide) |