Security model
Credentials stay on the phone. Tool results are data, never instructions, and third-party content taints the agent's turn. Anything that leaves the phone is declared and agreed to first.
PKU harness keeps your password for IAAA, the university's central login system, and it can act on your campus accounts. For this reason we want the system to behave, from your point of view, as if it had no server. No service the project runs can see your credentials, and nothing a plugin does is hidden from you.
Principles
- Credentials never leave the phone. The IAAA password, the mobile-token (手机令牌) secret, model API keys and remote plugin tokens are kept in the phone's keystore. They are never logged, never sent to a project server and never given to a plugin, whether it is a plugin document, a bundled plugin or a remote plugin.
- Plugins get responses, not sessions.
services.campus.requestreturns only the response. Session cookies and tokens stay inside the harness. When a request needs a signature, the signing also happens inside the harness (through thesignhook). - Tool results are data. The harness tells the model that tool results, plugin context and remote plugin output are data, and must not be followed as instructions.
- Effects are declared. Every tool declares one of four effects:
read,write,moneyorpublic. For each effect, you decide whether the agent may act directly. Scheduled runs can only read. - What leaves the phone is declared. The campus services a plugin uses (
scopes), the model provider, and thedisclosurelines for PKU harness Cloud are all shown to you before first use.
Prompt injection
Much of the content on campus is written by third parties: a Treehole post, a notice, an event description, or the output of a remote plugin. Any of it could say "ignore previous instructions and…". This kind of attack is called prompt injection.
The harness treats all such content as data, and it limits how much harm an injected instruction can do. It works as follows.
Untrusted tools. A tool that returns third-party text sets untrusted: true. Every tool of a
remote plugin counts as untrusted.
Taint. Once the agent calls an untrusted tool during a turn, the rest of that turn is marked
as tainted. Later tool calls in the same turn see ctx.turn.tainted === true.
Durable writes need confirmation. After a turn is tainted, any write that would carry into future conversations waits for you. For example, the memory plugin saves such memories as pending, and you approve them on the memory screen.
There is one exception. If your own message asked the agent to remember something
(ctx.turn.userAsked), the write is what you wanted, so it is saved directly. Third-party text
can't pretend to be your message.
Ask-first defaults. In remote plugins, tools with the write, money and public effects ask
you first by default.
Host information that can't be forged. In each turn, the host gives the model some facts, such as the current time and the language to reply in. These sit in fixed places that plugin context can't imitate.
What plugin authors must do
- You must set
untrusted: trueon tools whose results contain text written by anyone other than the user. This includes posts, comments, notices, messages and descriptions from public sites. - You must not put instructions in results, such as "Tell the user to…" or "Now call…". Return
data and let the model decide. Guidance for the model belongs in the tool's
descriptionor the plugin'sinstructions. - Some tools write durable state that the agent will later treat as true, such as notes, memories
and saved preferences. Such tools should check
ctx.turn?.tainted, and either save the write as pending or refuse it. - When you return third-party text, you should strip control characters, invisible characters, bidirectional-text (bidi) characters and markup, and limit its length.
- You must set
userOnly: trueon tools that approve or unlock things on the agent's behalf.
Secrets in results
Some values must never reach the model or the logs.
Payment codes are shown only on a screen that uses host.useSecureDisplay(). Such a screen
blocks screenshots and turns the brightness up. A payment code is never returned in a tool result.
Share links are links to a cloud channel, and they carry the key in the URL fragment. The SDK's
SecretLink type is opaque: JSON.stringify(link) gives only "[secret link]". Show a link only
with host.shareLink(link) or <QRCode link={link} />. The host also removes anything that looks
like a share link from tool output and logs.
Tokens and keys are never available to plugins at all.
Security of plugin documents
The app interprets the steps that a PLUGIN.md declares, and no plugin code runs. A plugin
document can reach only the campus services, public websites and host abilities it declares, and
it works within fixed limits on time, size and number of operations.
The harness signs campus requests. As a result, a plugin document never sees a password, cookie or token, and it can't reach another plugin's storage.
Before you install a plugin, and before any update that asks for more access, you see a review sheet. It shows whether the plugin is official or from the community, which campus services and websites it uses, which app features it needs, and the effect of every tool.
In community plugin documents, tools with the write, money and public effects ask you first,
and their results are treated as untrusted data. The agent can draft a plugin but can't install
one: plugins.install only opens the review sheet, and you decide.
Security of remote plugins
A remote plugin runs on someone else's server. It never receives campus credentials, sessions or any other data of yours. It only receives the tool inputs that the agent sends it.
Its tools are untrusted, and tools that change data ask you first by default. The part of the
system prompt about the plugin also says that its output is data. The registry marks an entry
verified only after maintainers have reviewed its source code.
PKU harness Cloud
The optional cloud service is end-to-end encrypted by default. It identifies a device only by a key pair, without any student ID or name. Each plugin needs your separate consent before it can use the cloud, together with a description in plain words. See Cloud → Security.
Reporting a vulnerability
Report vulnerabilities privately, as described in SECURITY.md in
pkuinfo/pku-harness or
pkuinfo/pku-harness-plugins. Please don't open
a public issue.
Campus sessions and IAAA
The harness keeps the campus credentials, signs in to each PKU service itself, and gives plugins the responses through `services.campus.request`. Plugins never see passwords or session tokens.
Build a plugin
How to choose between a PLUGIN.md document, a remote plugin and a bundled TypeScript plugin, which repositories to clone, and the order of work from your first tool to a marketplace listing.