Skip to content

feat(plugins): add NkAntony777/scansci-pdf academic paper retrieval plugin - #64

Open
NkAntony777 wants to merge 15 commits into
MiniMax-AI:mainfrom
NkAntony777:plugin/scansci-pdf
Open

NkAntony777 wants to merge 15 commits into
MiniMax-AI:mainfrom
NkAntony777:plugin/scansci-pdf

Conversation

@NkAntony777

@NkAntony777 NkAntony777 commented Sep 26, 2026 •

Copy link
Copy Markdown

Add ScanSci PDF: source-only academic paper retrieval plugin

Package and maintenance

This plugin supplies two Skills (scansci-pdf and scansci-sort) and a local
stdio MCP server for DOI/arXiv retrieval, search, citations and reading-list
triage. It vendors a patched source-only fork of upstream ScanSci PDF v1.18.0,
commit ec812f085f392a9b67768fbcad7ddeb340e0245f, under Apache-2.0.

The submitted plugin is 1.18.0-minimax.2; the vendored engine is
1.18.0.post2. It includes Python source and packaged helper scripts, not only
Markdown/JSON. It does not use a PyPI engine installation or a scansci-pdf
executable found on PATH. No precompiled engine extensions, browser binaries,
credentials or downloaded Tor executables are bundled.

Dependencies and startup

  • Node >=22 and Python >=3.11 are required. Install the hash-locked dependencies
    from vendor/scansci-pdf/requirements.lock into a prepared Python environment;
    browser workflows use requirements-browser.lock and an owner-installed browser.
  • mcp.json starts launch_secure.mjs, which probes a supported interpreter,
    binds its absolute path and launches run_secure.py with Python -I.
    SCANSCI_PDF_PYTHON can name the absolute interpreter of the prepared environment.
  • The launcher verifies delivered source hashes and installed dependency versions.
    These checks are local integrity checks, not upstream signatures or an
    interpreter/site-package authenticity attestation.
  • CLI and sorting helpers use that same packaged bootstrap from the user's project
    workspace. Direct Python invocation requires python -I .../run_secure.py.

Data and network behavior

Configuration, caches and scoped credential exports stay in the active project's
private .scansci-pdf/ directory, not an implicit home-directory engine state.
Python path/write checks reject existing symlink/junction escapes. Destructive
cache operations and workbook overwrite require explicit owner confirmation.

Requests contact scholarly APIs, publisher/CDN endpoints, enabled mirrors and
user-authorized institutional gateways. Users can select legal_only to restrict
retrieval sources. Unpaywall requires the user's real email. Institutional routes
require the user's own account/entitlement; no account or access is supplied.

Hosted transport requires public HTTPS, validates destinations at connection and
redirect boundaries, and retains hostname TLS validation. Explicit owner-configured
proxies remain supported. Local browser traffic uses an authenticated CONNECT guard;
remote browser services are unavailable in hosted mode. Paper content, links and
metadata are treated as untrusted external data, not instructions.

Response to the October 12 review

This revision targets the seven findings on 0acd104:

  1. Hosted native browser profiles are not reopened or cloned. Only scoped credential
    exports are restored; localStorage requires opt-in and an exact origin.
  2. Cookie identity/security fields must be complete and valid before restoration or
    migration. Incomplete legacy records require re-login rather than weakened defaults.
  3. Zotero API credentials use origin-confined transport. Storage origins are
    allowlisted; uploads do not receive API keys or follow redirects. One multipart
    stream is followed by checked API-origin registration.
  4. Browser launch sites propagate trusted configuration, enforce explicit executable
    selection and verify the actual program and digest, including Camoufox.
  5. CLI/log/error/MCP output uses shared secret redaction. Direct and bulk configuration
    saves use typed trust-change comparison and independent mutable defaults, and require
    actual boolean confirmation. A valid replacement for a corrupt stored pin requires
    confirmation too, rather than silently inheriting trust or permanently blocking repair.
  6. Isolated bootstrap startup and the delivered stdio process are exercised with the
    locked SDK, including its current and legacy schema attribute names.
  7. Main-repository exact-head Checks still require maintainer approval. This account has
    read-only access to the upstream repository; actual approval and re-run API requests
    were rejected with HTTP 403 on October 14. Local tests and successful fork runs do not
    substitute for upstream Checks.

Validation and explicit limits

Local results, reproducible checks and skipped cases are recorded in
REVIEW-NOTES.md; REVIEW-REPLY.md is the corresponding review response.
The submitted SHA and service-side workflow results are recorded separately in the PR.
These local documents do not claim upstream CI/CodeQL approval or a green upstream check.

  • Windows Python 3.11.17 and 3.13.16: each 1002 passed, 5 skipped, 0 failed with
    an approved installed Edge browser and local intercepted fixtures.
  • Delivered mcp.json -> Node -> isolated Python stdio: both versions pass, 18 tools.
  • Source/dependency verification passes; independent source rehash has 173 entries
    and no mismatches. Repository schema validation and static checks pass.
  • Root npm tests remain 336 passed, 6 failed, 3 skipped on this Windows host:
    one symlink-privilege failure and five unchanged octopus-meme-maker smoke assertions.
    No repository-wide green result is claimed.

Windows Python 3.11/3.13 and locally intercepted real Edge fixtures are the local
runtime coverage. Live institutional login, publisher entitlement, successful live
Zotero upload, real Camoufox/Firefox, and Linux/macOS runtime behavior are not
established by those fixtures. Python audit checks are not an OS sandbox and do not
claim resistance to concurrent replacement of filesystem ancestors.

This description supersedes the original PyPI/PATH-only packaging description: the
submitted package vendors and executes the verified source-only engine described here.

Submitted Head and Fork CI - October 14

The repaired commit is a31c76828bf1f31500fe10f7e4c475da53821f36, now pushed to this PR.
Its exact-head fork-hosted engine workflow is success:
https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38521185585

Fork CI matrix Passed Skipped Failed
Ubuntu / Python 3.11 1001 6 0
Ubuntu / Python 3.13 1001 6 0
Windows / Python 3.11 1005 2 0
Windows / Python 3.13 1005 2 0

All four jobs record the checked-out SHA, verify delivered source/dependency
locks, execute browser-enabled engine regressions and pass actual packaged
Node/isolated-Python MCP stdio acceptance (18 tools).
Linux skips two Windows junction cases, two approved-Edge-specific cases, the
absent second-checkout comparison and the optional Generic-strategy prefix case.
Windows skips only the last two. These are scoped fork results, not upstream
Checks or live-account/Zotero-upload evidence.

Local tests were also repeated from a clean Git archive of this submitted SHA:
Python 3.11.17 and 3.13.16 each give 1002 passed / 5 skipped / 0 failed on the
local Windows host; both actual delivered stdio checks pass. The root npm suite
still has the six unrelated/host-permission failures disclosed earlier.

Upstream release gate: this commit's upstream CI, CodeQL and engine-check
runs remain action_required; no upstream check runs are visible and the
engine workflow has zero jobs:
https://github.com/MiniMax-AI/MiniMax-Code-Plugins/actions/runs/38521089253

After refreshing the credentials needed to push the fork workflow, I tested
approval and re-run on that new-head upstream run again: both returned HTTP 403,
Must have admin rights to Repository. My upstream repository access remains
read-only. A maintainer must approve/run these workflows; fork success does not
substitute for successful upstream PR Checks.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…lugin

Wraps the upstream scansci-pdf engine (Rimagination/scansci-pdf, Apache-2.0,
v1.17.0) as two Skills (download/search/troubleshoot + large-list triage) and a
stdio MCP server (scansci-pdf run). Engine installed from PyPI, not vendored;
credentials and config stay local.
- add .minimax-plugin/plugin.json (MiniMax V1 manifest) required by marketplace validation
- rename mcp.json -> scansci-pdf.mcp.json and align with *.mcp.json schemaVersion format
- add icon.png referenced by the manifest
- re-encode icon.png to a clean 512x512 RGBA PNG (drops the non-standard
  caBX chunk carried from the source image, matching the standard chunk
  profile of icons already accepted by the marketplace)
- bump package version after content change per submission guide
…17.2

The description contained 'DOIs): classify' - a colon+space inside an
unquoted YAML scalar breaks frontmatter parsing (Marketplace V1 check
SKILL_FRONTMATTER_INVALID). Reworded to a comma construction; verified
both SKILL.md frontmatters parse with a YAML parser.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for exact current head b523ef43ec9fef79c83dc03efbdba07b689290d6.

Blocking supply-chain/security/host-contract issues:

  1. The public oa_url queue field is fetched with redirects and no loopback/private/link-local/metadata/DNS-rebinding policy. A controlled loopback PDF URL was accepted and requested. Enforce per-hop public HTTPS/IP validation at the actual connection boundary and add redirect/rebinding regressions.
  2. The MCP-exposed Tor installer downloads a tar and uses unchecked tarfile.extractall(), then marks discovered binaries executable. A ../ member was reproduced writing outside the target directory. Reject absolute/traversal/link members and verify a pinned signature/hash before extraction/execution.
  3. Plugin manifests say 1.17.2, but that upstream/PyPI version does not exist; README references 1.17.0, installation is unpinned, and MCP executes bare scansci-pdf from PATH. Pin an existing audited package plus integrity/provenance and bind the launched executable/version.
  4. springer_api_key is omitted from masking and can be returned by scansci_pdf_config; config/cookie state is written without restrictive mode (reproduced as 0644 under umask 022). Mask all credentials and use 0600/symlink-safe persistence.
  5. Core Skill workflows reference scripts/sort_finalize_writeback.py and scripts/sort_mdpi_res_batch.py, but those scripts are not packaged. The nonstandard scansci-pdf.mcp.json is also not validated by the repository’s root mcp.json contract. Ship and test the actual package/host layout.
  6. Cache/output/destructive paths lack workspace containment, symlink policy and confirmation; paper content is returned into model context without an explicit prompt-injection trust boundary.

Exact-head CI/CodeQL are action_required; the only visible check is [code]smith = SKIPPED, which is not evidence.

@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — updated PR #64 to commit f3d8b1c9529d3221b4b423d995897263f1da04b1 in response to your requested changes.

Thanks for the detailed review. This revision vendors and patches the engine directly, based
on the existing upstream v1.18.0 tag/commit, rather than relying on a nonexistent 1.17.2 release
or waiting for an upstream fix.

The actual connection layer now validates DNS answers and uses numeric destination addresses,
including redirects, configured HTTP/SOCKS proxies and a guarded local browser path. Tor uses
pinned archive hashes and checked extraction; installation needs explicit confirmation. Secrets
are masked, config/cookies have private atomic persistence, writes stay in the project with
symlink/junction checks, and destructive workbook/cache actions require confirmation. The
packaged helpers and root mcp.json are included in validation, and Skills declare the untrusted
content boundary. The launcher verifies source hashes/dependency versions and loads source
without trusting PATH engines or bytecode caches.

Local Windows evidence: 429 engine tests passed, 3 skipped; actual stdio MCP integration and
Chrome redirect/private-address checks passed; repository validation passed. A repeatable
4-worker/40-file HTTPS fixture completes every file and reduces TLS connections from 40 to 4.
Including write containment, median transport time was 385→340 ms locally and 1026→385 ms
with 20 ms connection delay. This is not a claim about live publisher latency or Cython speed.

REVIEW-NOTES.md contains the details and limitations: remote browser daemons/private-intranet
routes remain restricted, Firefox and other platforms need CI evidence, Tor extraction was
tested but no working circuit is claimed, and path checks are not a native OS sandbox. A scoped
Linux/Windows workflow is added; I do not count approval-pending GitHub jobs as passes.

Additional disclosure: the repository-wide npm test run is not green on this Windows host due to unrelated plugins' LF-only YAML/GBK encoding assumptions. Those files were not modified. The passing engine suite and schema validation are scoped evidence, not a claim that all repository tests or exact-head GitHub CI passed.

Please re-review the updated head when convenient.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for exact current head f3d8b1c9529d3221b4b423d995897263f1da04b1.

The unified public-HTTPS transport and source-hash checks improve the previous SSRF and vendoring findings, but this exact head still exposes multiple merge-blocking security surfaces:

  1. RemoteAssist is an unauthenticated LAN control surface. vendor/scansci-pdf/src/scansci_pdf/remote_assist.py:229-237 binds HTTPServer(("0.0.0.0", port), ...); focused probes received unauthenticated 200 responses from /api/status and /api/done. Bind to loopback by default; if LAN access is required, add explicit owner confirmation, short-lived authentication and CSRF protection. Escape dynamic publisher/browser_url values before inserting them into the HTML template.
  2. The optional web UI also defaults to unauthenticated 0.0.0.0. src/scansci_pdf/main.py:64-78 defaults web --host to 0.0.0.0; src/scansci_pdf/web.py:97-190 exposes unauthenticated /api/download, /api/search, and /api/status. Bind loopback by default or remove this entrypoint from the hosted package; add authentication, CSRF protection, and complete workspace/path containment for downloads.
  3. Proxy credentials can be disclosed in diagnostics and recommendations. vendor/.../sources/scoring.py:153-165,201-213 places raw SCANSCI_PDF_PROXY/config proxy URLs into report fields and error recommendations. This includes user:password@host forms and bypasses the otherwise improved masking path. Redact structurally at every MCP/CLI/health/recommendation/error boundary, including empty usernames, IPv6 and encoded credentials; never return raw proxy URLs.
  4. Browser login persists more credential state than necessary. vendor/.../browser_login.py:171-187 falls back from publisher cookies to the entire cookie jar; browser_cookies.py:108-150 persists all cookies and all discovered localStorage origins. This can retain unrelated IdP/SSO sessions and other domains. Restrict custom login destinations to an allowlist, save only the target-domain minimum cookie fields, disable localStorage persistence by default, and require explicit confirmation for any broader capture.
  5. The secure launcher is not self-contained in a clean environment. vendor/scansci-pdf/run_secure.py:52-83 imports packaging but vendor/scansci-pdf/pyproject.toml does not declare it as a direct dependency; isolated run_secure.py --verify fails with ModuleNotFoundError: packaging. Add it to the package dependency and lock/bootstrap contract, then run the launcher and actual stdio MCP in a clean environment. Version-only checks are not independent provenance for the executing distribution.
  6. Browser executable provenance remains uncontrolled. The browser backend accepts PATH-discovered or arbitrary configured executables without a release signature/hash and does not apply the same symlink/provenance gate consistently. Restrict to verified allowlisted binaries and document the install/update provenance.

Repository validation passed and the vendored source hash manifest matched, but Python tests, actual stdio MCP, browser/Tor integration, and launcher verification were not independently green in this review environment. [code]smith = SKIPPED was not used as evidence. Do not approve or merge until the network surfaces, credential persistence/redaction, launcher dependency contract, and exact-head runtime evidence are fixed.

NkAntony777 and others added 3 commits October 10, 2026 11:17
…enance

Addresses the six merge-blocking findings on the previous head.

RemoteAssist bound 0.0.0.0 and answered unauthenticated /api/status and
/api/done, so anyone on the LAN could drive the verification flow. It now
binds 127.0.0.1 unless the caller passes allow_lan=True *and* the config
sets remote_assist_lan, and every request carries a per-start bearer token
compared with hmac.compare_digest. The loopback bind is authenticated too:
every process on the host can reach 127.0.0.1, and one code path means auth
cannot be skipped by accident. POST /api/done additionally requires
X-Requested-With, which a cross-site form post cannot set. The page's
publisher and browser_url are html.escape'd before interpolation.

The optional web UI defaulted --host to 0.0.0.0 behind unauthenticated
/api/download, /api/search and /api/status. The default is now 127.0.0.1 and
the endpoints require a bearer token plus the same CSRF header.
/api/download resolves and containment-checks the returned path against the
configured output directory and refuses symlinks, instead of serving any
path the download layer returned.

This also fixes a pre-existing defect: _PAGE_TEMPLATE.format() raised
KeyError on the inline CSS braces, so the assist page returned 500 on every
request. Slots are filled explicitly instead.

Raw proxy URLs reached report fields, recommendations and error strings.
redact_proxy_url delegates to the existing config.mask_config_value rather
than adding another implementation, and covers the empty-username form
that helper missed; mask_config_value itself now masks that form too. No raw
proxy URL is returned on any boundary, including the ProxyError path.

Browser login no longer falls back to the entire cookie jar, and cookies
and localStorage are persisted scoped to the target domain; localStorage is
opt-in. Custom login destinations are allowlisted, and the publisher-cookie
match is dot-anchored so evil-sciencedirect.com is no longer accepted.

run_secure.py imported packaging without declaring it, so an isolated
--verify failed with ModuleNotFoundError. It is now a declared dependency,
and a missing one produces an actionable install message. --verify also
states what it actually proves: local hashes and installed dependency
versions, not upstream provenance.

Browser executables were accepted from PATH or arbitrary config. A new
provenance gate requires an allowlisted system-browser root or the engine
cache, reuses the existing no_symlinks gate, and honours a pinned digest;
SCANSCI_PDF_TRUST_BROWSER_PATH is the explicit opt-out. Provenance here is
"the file the owner approved", not a vendor code-signature check.

493 tests pass. The 2 remaining failures need the optional camoufox and
cloakbrowser backends and fail identically before these changes. No live
SSO round-trip was exercised: cookie scoping is covered by unit tests.
…t survived

Follow-up to the previous head, addressing what a second review could still
reach.

Proxy credentials still escaped through paths other than network_diagnose.
The failure-tips builder in sources/__init__.py interpolated raw proxy URLs
into tips returned to the model and the CLI, config-cmd echoed the value it
was given and printed network_proxy and browser_static_proxy directly, and a
credential with no scheme (user:pass@host) was passed through unredacted.
mask_config_value is now the single implementation and covers the schemeless
form; the duplicate regex in redact_proxy_url is gone. One test pinned the
schemeless pass-through; that expectation encoded the leak and was inverted.

Cookie scoping stayed wider than the target domain: scoped_cookies kept every
known publisher's cookie, so one login left a live session behind for all of
them. It is now target-domain only. WebVPN/EZProxy are unaffected because
they proxy the publisher under the proxy hostname, which is the target.
_is_publisher_cookie is dot-anchored so evil-sciencedirect.com cannot match.
browser_persist_localstorage joins the confirmed:true set in strict mode, so
the model can no longer enable it without owner confirmation.

Browser provenance had three bypasses. channel="chrome" — the default backend
path — launched whatever Playwright resolved, ungated; the browser is now
resolved through the gate and passed as executable_path, and the bundled
Chromium retry is gated too. CLOAKBROWSER_BINARY_PATH was accepted without
validation. And SCANSCI_PDF_TRUST_BROWSER_PATH returned before the digest
comparison, so the escape hatch also disabled hashing; it now relaxes only the
allowlist, never a pinned digest. Cache-root binaries require a pinned digest
because the cache is writable at runtime. Per-user installs are reachable but
also require a digest, so a per-user Chrome is a configuration step rather
than a dead end.

Token exposure was wider than the authenticated routes. The web page rendered
the bearer token into an inline script while loading Tailwind and Alpine from
two CDNs, any of which could read it and call the local API. The token is no
longer rendered at all: the ?t= link hands it over once as an HttpOnly,
SameSite=strict cookie, and page JavaScript authenticates via that cookie. A
CSP now constrains script/style/connect sources on every response, and
/docs, /redoc and /openapi.json are disabled on an authenticated surface.
uvicorn's access log records the full request target, so it no longer runs at
all for the web command. RemoteAssist keeps its inline token but serves a CSP
that permits no external script, stops logging the authenticated URL, and
refuses an expired token even when the constant-time comparison would match.

517 tests pass. The 4 remaining failures need the optional camoufox,
cloakbrowser and pymupdf4llm packages and fail identically before these
changes. Manifest regenerated and verified in both directions (167 entries,
no mismatches, no tracked source missing). No live SSO round-trip was
exercised; cookie scoping is covered by unit tests.
…arty

Program Files was treated as a user-writable root, so the stock Chrome and Edge installs were rejected without a pinned digest, and a rejected bundled Chromium aborted a browser that had already passed the gate.

The web UI no longer loads third-party scripts. Its own code is nonce-bound, and setup --show masks proxy passwords.

Co-authored-by: Cursor <cursoragent@cursor.com>
@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — updated PR #64 to commit 9e83a1bccc8a48e7b0f58a0edee746e207fb3968 in response to your requested changes on f3d8b1c.

  1. RemoteAssist. The server binds 127.0.0.1 unless the caller passes allow_lan=True and the config sets remote_assist_lan. Every request needs a per-start bearer token, compared with hmac.compare_digest, and the token expires after 15 minutes. POST /api/done also requires X-Requested-With. publisher and browser_url are escaped before they are inserted into the page. The access URL is printed for the operator and is not written to the log. The page CSP allows no external script.

  2. Web UI. web --host defaults to 127.0.0.1. API routes require the bearer token, and POST routes also require X-Requested-With. /api/download serves a file only when the resolved path stays inside output_dir and no ancestor is a symlink or junction. /docs, /redoc, and /openapi.json are disabled. The ?t= link is a one-time handover into an HttpOnly SameSite=strict cookie; the token is not rendered into the page. The page no longer loads third-party scripts, and its own script is nonce-bound. The uvicorn access log is off, so the handover URL is not recorded.

  3. Proxy credentials. Passwords are redacted on network diagnostics, download-failure guidance, config-cmd, and setup --show, including empty usernames, IPv6 hosts, and the schemeless user:pass@host form. Where a suggestion would otherwise paste a password into config_set, the message tells the operator to type the address instead.

  4. Browser login state. Persisted cookies are limited to the login target's domain and to name, value, domain, and path. localStorage stays off unless browser_persist_localstorage is set, and in strict mode that key requires confirmed: true. Custom login destinations are allowlisted. Institutional WebVPN, EZProxy, and CARSI flows still use the operator's configured origin.

  5. Launcher dependency. packaging is a direct dependency and is pinned in requirements.lock. A missing install exits with an install command instead of ModuleNotFoundError. --verify reports local file hashes and installed dependency versions; it does not claim upstream provenance for the executing distribution.

  6. Browser executables. Configured paths, CLOAKBROWSER_BINARY_PATH, the chrome channel, and the bundled Chromium fallback all go through the same allowlist, symlink, and digest gate. Program Files installs are accepted without a digest. Per-user (LOCALAPPDATA) and cache binaries require a pinned digest. SCANSCI_PDF_TRUST_BROWSER_PATH widens the allowed location and does not skip a pinned digest. A rejected bundled binary does not discard a local browser that already passed.

Local evidence: the provenance, token-exposure, and proxy-redaction tests passed (60 passed, 1 skipped because this environment cannot create symlinks). I am not counting approval-pending GitHub jobs as passes.

Please re-review the updated head when convenient.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for exact current head 9e83a1bccc8a48e7b0f58a0edee746e207fb3968.

The new head closes important portions of the prior review: RemoteAssist and the optional Web UI default to loopback with auth/CSRF, proxy diagnostics in scoring.py are redacted, packaging is declared and locked, and source-hash verification is stronger. The following exact-head blockers remain:

  1. Browser credential persistence still has bypass paths. Although browser_login.py has scoped/minimal helpers, restore accepts all cookies/localStorage from browser_state.json without reapplying domain scope or the localStorage opt-in. Other login flows directly persist full context.cookies(), including auth.py, instsci.py, and _publisher_strategies_core.py through session_store.py. This can retain unrelated IdP/SSO and other-publisher sessions. Route every persistence/restore path through target-domain filtering and minimum fields; default localStorage persistence off and enforce the same policy on restore.
  2. Camoufox launch paths are not provenance-gated. browser_backend.py:588-691 calls NewBrowser(...) directly; strict mode only checks the installation directory exists (:736-741, :786-789) and does not verify the actual executable with the Patchright-style symlink/signature/hash gate. An altered binary inside the directory can therefore execute despite the new provenance policy. Gate both ordinary and persistent Camoufox launches on the actual executable and verified provenance.
  3. Exact-head runtime evidence is absent. GitHub reports zero check-runs/statuses for this head (gh pr checks reports no checks), so the workflow file and producer's local “429 passed” claim do not establish the Python suite, clean launcher, stdio MCP, browser, Tor, or Windows/Ubuntu contract. Run and retain the exact-head workflow results before approval.
  4. The explicit non-loopback streamable HTTP mode remains unauthenticated. main.py:50-54 launches mcp_app.streamable_http_app() directly when --mode streamable_http --host 0.0.0.0 is supplied, bypassing the Web UI's auth middleware. Either reject non-loopback HTTP without an authenticated transport or enforce auth at this entrypoint.
  5. Proxy redaction is not a single egress boundary. Raw proxy values still reach at least auth.py logging/launch arguments and other source failure/result paths outside scoring.py. Ensure logs, process arguments, MCP results and exception/source-failure objects cannot contain credential-bearing proxy URLs.

node validation/source-hash checks are positive, but they do not close these runtime and provenance gaps. [code]smith = SKIPPED was not used as evidence. Do not merge this head.

… 3.11 floor

The browser and base locks pinned networkx==3.7 and numpy==2.5.3, which
exclude Python 3.11 (networkx 3.7 via file-level Requires-Python, numpy
2.5.x by shipping only cp312+ wheels). requires-python is >=3.11 and the
engine CI matrix includes 3.11, so the first real CI run failed at
dependency install. Repin to the highest 3.11-compatible versions
(networkx 3.6.1, numpy 2.4.6) with full PyPI hashes in both locks and
regenerate SOURCE-HASHES.json.

Verified: hash-locked installs on Python 3.11 and 3.13; full engine
suite 767 passed / 5 skipped on both.
The first Linux CI run exposed four test-harness assumptions that held
only on a wide Windows console:

- Typer help assertions matched raw stdout; at CI's 80-column default the
  Rich output wraps and styles those substrings away. Normalize ANSI and
  whitespace before asserting.
- Two stdio lifecycle tests closed stdin by hand before communicate();
  POSIX Popen.communicate() then flushes the closed stdin and raises.
  communicate(input=...) writes, flushes and closes stdin itself.
- The taskkill tree-kill test relied on the host being Windows to select
  the branch; patch os.name like the posix test does.
- The real-Chromium guard ignored SCANSCI_AUDIT_BROWSER and hardcoded the
  Windows Chrome path, so the ubuntu leg skipped the round trip. Read the
  env var (Windows default unchanged) and point the ubuntu matrix at
  /usr/bin/google-chrome.

Full suite 767 passed / 5 skipped at COLUMNS=80 with the real Chrome
round trip enabled.
…e real Chrome binary

Two leftovers from the first Linux run:

- The staged stdio handshake test closed stdin immediately after the
  write, racing the server's EOF shutdown against the tools/list answer
  on fast hosts: initialize was answered, tools was dropped. Read the
  responses with stdin still open (a real client waits before closing),
  then close to confirm the clean exit.
- The ubuntu matrix pointed at /usr/bin/google-chrome, which is a symlink
  to /opt/google/chrome/chrome; the provenance gate refuses symlinks by
  design, so the real-browser round trip failed with SecurityError.
  Point at the real binary.
On POSIX, Popen.communicate() flushes self.stdin unconditionally, so
calling it after a manual stdin.close() raises 'flush of closed file'
even with input=None — exactly what the handshake test hit after waiting
for the tools response. Drain both pipes with reader threads and await
the child with wait() instead; the finally path gets the same treatment
(a second communicate() after the first failed mid-state raised
AttributeError: no attribute '_fileobj2output').
@NkAntony777

Copy link
Copy Markdown
Author

Reply to the third-round review (head 9e83a1b). Fixes landed in c357d40 (credential/launcher gaps), 75c3783 (dependency-lock Python 3.11 floor), a58d0f5 + 3a7ec91 + 0acd104 (regressions made Linux/width-robust; the first Linux run ever exposed several Windows-console-only assumptions).

  1. Browser credential persistence — every save/restore/handoff path now routes through one policy (scoped_cookies / credentials_for_target in browser_cookies.py):
    • browser_login._restore_state re-derives the scope from operator config only; the target_url inside browser_state.json is used solely as a host-consistency check (mismatch ⇒ nothing is restored). localStorage restore additionally requires the opt-in flag and per-origin target match.
    • CookieStore.save/load/load_into (session_store.py) are fail-closed: without target_url nothing is written or returned. Security attributes (httpOnly/secure/sameSite/expires) survive the round trip — covered by test_credential_attributes.py and a real approved-Chrome round trip.
    • _save_all_cookie_formats, instsci.py, publisher_batch.py (shared cache write + cross-context handoff), and browser_engine.py's solution cookies are all scoped; an unknown publisher without a target URL persists an empty set, never the jar.
  2. Camoufox provenance — both _launch_camoufox and _launch_camoufox_persistent resolve the actual program via resolve_camoufox_executable (deterministic candidate walk, symlink/junction refusal, and the same allowlist + pinned-digest verify_browser_executable gate Patchright uses), then pass it explicitly as executable_path so Camoufox's own launcher cannot substitute a different binary. The install-directory existence check is gone. Unit-level regressions in test_camoufox_backend.py; no real Camoufox/Firefox launch is claimed.
  3. Exact-head runtime evidence — the workflow now records git rev-parse HEAD, verifies the delivered source manifest (run_secure.py --verify), and enables a real approved-browser round trip. Upstream workflows still await approval for this fork's PRs, so the exact-head runs live on our fork:
    • 0acd104: https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38120232031 (ubuntu+windows × 3.11+3.13)
    • For the record, the earlier same-branch runs failed on test-harness assumptions the first Linux execution exposed, each fixed in the next commit: c357d40 (run 38118460256) at dependency install — both locks pinned networkx==3.7/numpy==2.5.3, which exclude the declared Python 3.11 floor; 75c3783 repinned them (networkx 3.6.1, numpy 2.4.6, full PyPI hashes). a58d0f5 (run 38119643261) fixed 80-column Rich wrapping in help assertions, POSIX communicate() misuse, host-platform-dependent branch mocking, and a real-Chromium guard that ignored SCANSCI_AUDIT_BROWSER; by that run ubuntu+3.13 was already fully green. 3a7ec91 (run 38119968098) pointed the ubuntu matrix at the real /opt/google/chrome/chrome binary (the /usr/bin/google-chrome symlink is refused by the provenance gate — by design) and 0acd104 removed a manual-close-then-communicate() pattern in the stdio handshake test (POSIX communicate() flushes a closed stdin unconditionally).
  4. streamable HTTP auth — _require_loopback_host refuses non-loopback binds at startup ("no request authentication"); the WEB mode now shares web.py::start_web_server (token + CSRF + header policy) with the web subcommand, so no weaker duplicate entry point exists.
  5. Proxy egress boundary — the auth.py connector log line is masked, and a single boundary now covers the surfaces you listed: proxy_redaction.py installs a logging.Filter before any handler sees a record (including exception chains), a recursive redactor wraps the JSON serialization boundary in server.py, and a redacting Rich console covers rendered output; log.get_logger routes all engine logging through it. Regressions in test_proxy_redaction.py cover log lines, exception chains, result objects, dict keys, and nested structures.

Local verification at this head: 767 passed / 5 skipped on Windows Python 3.11 and 3.13 with COLUMNS=80 and the real Chrome round trip enabled; hash-locked installs verified on both versions; source manifest independently recomputed (172 entries, 0 mismatches).

Not claimed: real Camoufox/Firefox launch evidence, Linux desktop testing beyond CI, or physical power-loss durability.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for exact current head 0acd104afbfc088b3411cedd45f3f3a803c8b6a8.

The head adds substantial hardening and tests, but exact-head review still found merge-blocking credential and trust-boundary gaps:

  1. Native persistent browser profiles bypass the scoped credential policy. browser_engine.py:331-343 intentionally preserves all cookies/localStorage across restarts, and real publisher/auth/batch flows use this persistent context. Those browser-native profile writes and restores do not pass through target-domain/minimum-field filtering or the browser_persist_localstorage opt-in, so unrelated IdP/SSO state and legacy profile credentials remain persistent.
  2. Legacy cookie migration accepts partially specified security state. browser_cookies.py:320-335 treats a record as non-legacy when it contains any one security field, after which missing attributes are filled with defaults. A cookie containing only secure:true, or only expires, was restored with missing httpOnly/other fields weakened. Require the complete security-field set or discard/migrate explicitly.
  3. Zotero API credentials can cross origins on redirects. zotero.py:142-149,323-361 uses ordinary requests.post with Zotero-API-Key, while credential_transport.py:7-10 confines only Elsevier/Authorization headers. A controlled requests redirect from api.zotero.org to another origin retained the synthetic Zotero-API-Key. Use an origin-confined session that also covers Zotero headers; independently allowlist and confine any response-supplied upload URL/authorization secret.
  4. Browser provenance configuration is not enforced at every launch site. Multiple real calls to browser_backend.launch/get_persistent_context omit config, bypassing owner-selected backend/executable/digest pins even though the lower-level provenance gate is stronger. Propagate one trusted config through every Patchright/Camoufox/persistent launch path and assert the actual executable.
  5. Sensitive-output and trust-write boundaries remain incomplete. elsevier-check prints the full institutional token, config-cmd echoes arbitrary secret values, and several trust-sensitive proxy/backend settings can still be saved without the unified owner-confirmation gate. Apply one global secret scrubber to CLI/log/error/MCP egress and route every trust-sensitive update through a mandatory confirmation boundary.
  6. The secure launcher contract is not fully isolated or portable. mcp.json invokes bare python, which is absent on supported environments that expose only python3; run_secure.py does not independently enforce the declared Python >=3.11 floor or isolate inherited interpreter startup state. Bind a supported interpreter/bootstrap path and add a clean-environment stdio handshake.
  7. No main-repository exact-head Checks are visible. statusCheckRollup is empty, so the claimed Python 3.11/3.13 suite, stdio handshake, browser integration and credential regressions are not service-side evidence for this PR head.

Source hashes and syntax checks are positive, but they do not close these runtime boundaries. Do not approve or merge this head.

@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — CI permissions clarification, verified on 2026-10-14.

I do not have permission to approve/start the approval-gated CI runs or re-run workflows in the upstream MiniMax-AI/MiniMax-Code-Plugins repository. GitHub reports read-only access for my account (pull: true, push/maintain/admin: false). I also actually tried both the workflow-run approval and re-run API endpoints for this PR's current-head engine-check run; both returned HTTP 403: Must have admin rights to Repository.

For the current PR head 0acd104afbfc088b3411cedd45f3f3a803c8b6a8, the upstream ScanSci PDF engine checks, CI, and CodeQL runs remain action_required, not passing or failed test results. The blocked engine-check run is:
https://github.com/MiniMax-AI/MiniMax-Code-Plugins/actions/runs/38120231356

I can trigger workflows in my own fork. The corresponding exact-head engine-check run there is verified success:
https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38120232031

That fork-hosted result is separate evidence; it does not unblock or substitute for the upstream PR checks. Could a maintainer with the necessary permissions please approve the pending upstream workflows for this head and re-run them if needed? I cannot remove this approval gate myself.

@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — the October 12 review fixes are now committed and pushed in a31c76828bf1f31500fe10f7e4c475da53821f36 (previous reviewed head: 0acd104). I also replaced the stale PR description with the actual source-only vendoring/bootstrap architecture and current validation limits.

Implementation points 1-6:

  1. Native profiles and legacy cookies: hosted persistent-profile APIs now create non-persistent contexts and never reopen or clone native profiles. Only scoped credential exports may return across runs. Cookies require the complete, typed identity/security field set; partial legacy records are discarded. Real installed Edge restart fixtures verify unrelated SSO/localStorage do not return while permitted target cookies retain their security attributes. localStorage opt-in uses the exact origin.
  2. Zotero: API requests use origin-confined transport including Zotero-API-Key, with redirects disabled. Storage origins are explicitly allowlisted and never receive the API key. Full upload is one streamed prefix/file/suffix envelope, followed by checked API-origin registration. Response-provided upload authorization is redacted from errors. This is fixture evidence, not a claim of a successful live Zotero upload.
  3. Browser provenance: every production launch supplies trusted config; missing config, missing selected backend/program and pin mismatches fail closed. Cache identities include config. Camoufox honors the explicit verified program and rejects raw options that could replace it. Real Camoufox/Firefox execution is not claimed.
  4. Egress and trust writes: CLI/stream/log/exception/Rich/MCP egress shares secret scrubbing. Direct and bulk saves use the same typed trust-change/actual-boolean confirmation gate, independent mutable defaults and confirmed valid repair of corrupt pins. Alternate school/gateway setters are atomic and require explicit confirmation through both CLI and the actual MCP schema.
  5. Isolated startup: packaged Node bootstrap supports python3-only installations and an absolute SCANSCI_PDF_PYTHON, probes/binds Python >=3.11 and launches with -I. The Python entry independently enforces isolation/version. The source loader closes the entire engine namespace to unmanifested/legacy-bytecode imports. Polluted startup/shadow-module canaries and actual delivered stdio pass with the locked SDK's current and legacy schema names.

Fresh validation, repeated both before commit and from a clean Git archive of the submitted commit:

  • Windows Python 3.11.17 and 3.13.16: 1002 passed / 5 skipped / 0 failed each, with real installed Edge and locally intercepted fixtures. Committed-archive runs took 157.60s and 157.79s respectively.
  • Delivered mcp.json -> Node -> isolated Python stdio: 18 tools, both interpreters; strict school confirmation/atomic setup, parsing, path refusal, Tor confirmation, masked config, helper overwrite confirmation and tamper rejection exercised.
  • Source/dependency verification and independent rehash: 173 entries, no mismatches or omissions. Node syntax, whitespace and repository validation pass (29 hosted plugins).
  • Root npm test is not green on this Windows host: 336 passed / 6 failed / 3 skipped. One symlink EPERM and five unchanged octopus-meme-maker platform/line-ending assertions; those files are untouched.

New-head fork CI is success (all four jobs):
https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38521185585

Ubuntu Python 3.11/3.13 each: 1001 passed / 6 skipped / 0 failed. Windows Python 3.11/3.13 each: 1005 passed / 2 skipped / 0 failed. All four record the exact SHA, verify source/dependency locks, run browser-enabled regressions and pass delivered MCP stdio acceptance. Linux skips two Windows junction cases, two approved-Edge-specific cases, the absent second checkout and the optional Generic prefix case; Windows skips only the last two. This supersedes the older fork result for 0acd104; it covers the newly submitted repair, but it is not an upstream PR Check.

Point 7 remains an upstream release gate, not a code fix I can authorize. The new-head upstream engine checks, CI and CodeQL are action_required, with no visible check runs; the engine workflow has zero jobs:
https://github.com/MiniMax-AI/MiniMax-Code-Plugins/actions/runs/38521089253

I re-tested approval and re-run on that new-head run after refreshing my fork workflow credentials: both still return HTTP 403: Must have admin rights to Repository. My upstream access is read-only. Please approve/run the pending upstream exact-head workflows and re-review this new head. Fork-hosted results are separate evidence and do not substitute for upstream Checks.

Other explicit limits: no live institutional account login/publisher entitlement, physical power-loss test, macOS coverage or native OS sandbox is claimed. Details and reproducible checks are in REVIEW-NOTES.md and REVIEW-REPLY.md.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for exact current head a31c76828bf1f31500fe10f7e4c475da53821f36.

The current diff appears to close the previously reported native-profile persistence, cookie-scope/migration, Zotero upload-origin/credential, browser-executable provenance, trust-sensitive config, secret-egress, and packaged launcher boundaries. A focused exact-head run of the new browser/config/Zotero/source-loader regressions reported 198 passed, 3 skipped, 0 failed on macOS/Python 3.13.

One release blocker remains: this exact head has no upstream GitHub Checks at all. The submitted workflow declares Ubuntu/Windows × Python 3.11/3.13 coverage and the local notes explicitly distinguish local/fork runs from upstream evidence, but GitHub currently exposes no completed ScanSci workflow, main CI, or CodeQL job for a31c7682.... Run the approval-gated workflows on this exact head and retain successful cross-platform results before approval. Local results and author-reported Windows runs cannot replace repository-owned exact-head Checks.

No previously closed code finding is being re-opened here; this review is limited to the missing exact-head release evidence. [code]smith = SKIPPED, if it appears, is not test evidence.

@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — thank you for the fifth-round review, and for independently running the new browser/config/Zotero/source-loader regressions on macOS/Python 3.13.

The head is unchanged since your review: a31c76828bf1f31500fe10f7e4c475da53821f36. The single remaining blocker — no upstream Checks on this exact head — cannot be closed from my side, because it is gated on an action only a base-repository maintainer can perform:

  • PR feat(plugins): add NkAntony777/scansci-pdf academic paper retrieval plugin #64's branch lives in the fork NkAntony777/MiniMax-Code-Plugins. For pull_request workflows from a fork, GitHub requires a maintainer of MiniMax-AI/MiniMax-Code-Plugins to approve each run before it executes; the PR author cannot self-approve.
  • My account's API-reported permissions on the upstream repository are read-only (pull: true; push/maintain/admin: false).
  • Re-verified today (2026-10-15): POST /repos/MiniMax-AI/MiniMax-Code-Plugins/actions/runs/38521089253/approve still returns HTTP 403: Must have admin rights to Repository. — same result as reported on 2026-10-14.

The three runs pending approval for this exact head, all currently action_required:

Each run page exposes an "Approve and run" control (they also appear under "Awaiting approval" in the Actions tab). Approving them will produce exactly the repository-owned, exact-head, cross-platform Checks your review requires — no code change is involved.

For the record: the same workflow file at this exact commit SHA is green when hosted in the fork — all four jobs (Ubuntu/Windows × Python 3.11/3.13), each job recording the verified SHA and re-verifying the source/dependency locks: https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38521185585 . As stated previously, this remains separate evidence and not a substitute for the upstream Checks.

Please approve the three pending runs when convenient; I will confirm the results here as soon as they complete.

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve exact current head a31c76828bf1f31500fe10f7e4c475da53821f36.

The previously reported browser profile/cookie scope, Zotero upload-origin and credential confinement, browser executable provenance, trust-sensitive config, secret-egress, and packaged launcher concerns are closed by the current diff. Local focused coverage is 198 passed / 3 skipped / 0 failed, and the repository-owned exact-head Checks are now green for CI, CodeQL, and ScanSci Ubuntu/Windows Python 3.11/3.13. The PR is MERGEABLE/CLEAN at this exact head.

…the official dev environment

15.0.24 was removed from the primary dist.torproject.org mirror and 15.0.25 is a
security update (Firefox fixes, Tor 0.4.9.14, OpenSSL 3.5.9). All six TOR_HASHES pins
come from the release sha256sums-signed-build.txt fetched from both mirrors; the real
Windows x86_64 archive matched the new pin and passed guarded extraction. README now
defines the official CPython 3.11 .venv provisioned from requirements-browser.lock,
mirroring CI. Full suite: 1002 passed / 5 skipped / 0 failed (147.13s, real Edge).
@NkAntony777

Copy link
Copy Markdown
Author

@hetaoBackend — confirming results, plus one new commit.

Previous head a31c768 — the three upstream Checks you asked for are now green (runs became available after the approval):

New head bc57519b4b28fcf6b780b1409a7dcee57baa460e — a single supply-chain commit: the Tor Expert Bundle pin moves 15.0.24 → 15.0.25. Two reasons: 15.0.24 was removed from the primary dist.torproject.org mirror (directory 404 as of 2026-10-15; only the archive mirror still hosts it), and 15.0.25 is a security update (Firefox security fixes, Tor 0.4.9.14, OpenSSL 3.5.9). Evidence for the bump:

  • All six TOR_HASHES values come from the release sha256sums-signed-build.txt, fetched independently from both mirrors with identical bytes; pins changed in source only.
  • The official Windows x86_64 archive (22,507,599 bytes) was downloaded from the primary mirror: SHA-256 c1988c701de2f24f7d098d1d6d7f507b58f27740d097f29c246e0524d639b252 matched the new pin, and guarded extraction plus installed-file verification passed against the shipped code.
  • Source manifest regenerated (173 entries, only embedded_tor.py changed); run_secure.py --verify passes with source manifest and dependency lock verified; delivered MCP package check passes (18 tools, stdio, path-escape block, tamper rejection).
  • Full suite in the documented official dev environment (CPython 3.11.17 venv, hash-locked requirements-browser.lock, real installed Edge): 1002 passed / 5 skipped / 0 failed in 147.13 s — identical counts to a31c768. README.md/README.zh-CN.md now define that environment. Details in REVIEW-NOTES.md ("Tor expert-bundle pin refresh").

This new head's upstream runs are action_required again (same approval gate as before):

The exact-head fork-hosted engine run is in progress: https://github.com/NkAntony777/MiniMax-Code-Plugins/actions/runs/38677749671 (as always, separate evidence, not an upstream Check). Please approve the three pending upstream runs when convenient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants