Releases and the jar scanner
How uploaded jars are scanned, what the scanner looks for and what its verdicts mean.
Uploading a release
Jars are optional on paid listings and required on free ones. Open the Releases tab of your listing, choose the file and give it a version such as 1.4.2.
- The file must be a real zip and at most 50 MB.
- The version is letters, digits and
.-+_, up to 32 characters, and unique among the releases that were not rejected. - Notes of up to 500 characters are optional.
Peakstone stores the file with its SHA-256, runs the scanner and puts the release in the review queue. Subscribers can download a release only after a reviewer approves it. Each release shows its review status on the Releases tab, and you can always download your own files.
The jar scanner
Every upload is checked by peakstone-scan, a static scanner written in Rust that looks for malware and backdoor indicators. It reads class files and archive entries; it never runs anything, never extracts to disk and never trusts a size field from the archive.
Every finding says where (path) and why (detail and evidence), so a reviewer can confirm or dismiss it quickly. If the scanner is unavailable or fails, the upload still goes through and the release is stored without a report; it is reviewed by hand either way.
Verdicts and score
Each finding has a severity worth a number of points. The score is the sum over all findings, capped at 100, and the verdict follows from it.
| Severity | Points |
|---|---|
| Critical | 60 |
| High | 25 |
| Medium | 10 |
| Low | 3 |
| Info | 0 |
| Verdict | When |
|---|---|
| Malicious | At least one critical finding |
| Suspicious | Score of 40 or more |
| Review | Score of 10 or more |
| Clean | Anything lower |
| Error | The file could not be read as a zip |
Because the score is a plain sum, several findings of the same rule add up. A large library jar with a few native-code and process findings can reach suspicious. That is the intended signal that a human should look, not an accusation. Only a critical finding means malicious.
What it checks
Class rules combine facts within a single class, which keeps false positives low. The main ones, with their usual severity:
| Rule | Severity | What it looks for |
|---|---|---|
known-malware | Critical | Public indicators of known campaigns such as fractureiser, and operator-backdoor chat triggers in a class that also grants operator status. |
op-backdoor | Critical | Granting or removing operator status from a chat or command event. Medium without the event. |
token-grabber | Critical | Paths used by credential stealers, such as browser and launcher token stores. |
remote-class-loading | High | Defining classes at runtime. Critical when the same class also downloads and decodes data. |
process-exec | High | Running operating-system processes. |
console-dispatch-from-chat | High | Dispatching commands from chat events or as the console. |
native-code | Medium | Loading native libraries, or shipping .so, .dll and similar files. |
discord-webhook | Medium | A complete Discord webhook URL embedded in the code. |
script-engine | Medium | Use of javax.script. |
hardcoded-ip | Low | Public IPv4 addresses in string literals. |
unsafe | Low | References to sun.misc.Unsafe. |
It also checks the archive and the plugin descriptor:
- Zip bombs (critical): too much uncompressed data, too many entries or an extreme compression ratio. Nothing is decompressed once this trips.
- Path traversal (high): absolute entry names or
..segments. - Hidden class files and disguised archives, encrypted entries and duplicate entry names that hide each other.
- Obfuscation: jars whose class names or strings are mostly meaningless or encrypted-looking, which a reviewer then reads more closely.
- The descriptor (
paper-plugin.yml,plugin.yml,velocity-plugin.jsonorbungee.ymlat the jar root): missing or unparsable, or naming a main class that is not in the jar. - Nested jars are scanned to a limited depth (2 by default).
Findings are sorted by severity, and the same rule on the same path is reported once. At most 500 are listed; the score and the verdict are computed before that cut.
Shaded libraries
Widely shaded libraries legitimately trip some rules: bStats and SQLite probe the operating system, Netty and Guava use Unsafe, dependency loaders download jars. The scanner knows a list of these and lowers only the rules each one legitimately triggers, usually to info. The match works on relocated copies too, such as com/author/plugin/libs/org/bstats/.
What it cannot do
- It is static and works on one class at a time. Strings assembled or decrypted at runtime are not followed, which is why obfuscation is flagged.
- Native code is flagged, not analysed.
- Only the constant pool is read, not bytecode, so a class that references an API shows up even if the call is never reached.
- Its indicator list is short and curated. That is why every release is also looked at by a person.
The source and the full rule list are in scanner/ of the Peakstone repository. To list a plugin, start with Getting started as an author.
Report format
The scanner prints one JSON object. Peakstone stores the verdict, the score, the first 200 findings and the detected plugin descriptor with the release, and the reviewer sees them in the queue.
{
"scanner": "peakstone-scan",
"verdict": "review",
"score": 10,
"plugin": { "kind": "paper", "name": "MyPlugin", "version": "1.4.2", "main": "com.example.myplugin.MyPlugin" },
"findings": [
{
"rule": "native-code",
"severity": "medium",
"path": "libs/natives.jar!/lib/native.so",
"detail": "...why, in a sentence",
"evidence": "the literal trigger"
}
]
}