Add username prefix requirement for plugin filenames
This commit is contained in:
@@ -154,6 +154,12 @@ for (const file of changedFiles) {
|
|||||||
|
|
||||||
const pluginId = file.replace('plugins/', '').replace('.json', '');
|
const pluginId = file.replace('plugins/', '').replace('.json', '');
|
||||||
|
|
||||||
|
// Filename must be prefixed with username
|
||||||
|
if (!pluginId.startsWith(`${prAuthor}-`)) {
|
||||||
|
errors.push(`❌ ${file}: Filename must start with your username "${prAuthor}-" (e.g., "${prAuthor}-my-plugin.json")`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if file was added or removed
|
// Check if file was added or removed
|
||||||
let isAdded = false;
|
let isAdded = false;
|
||||||
let isRemoved = false;
|
let isRemoved = false;
|
||||||
|
|||||||
137
SECURITY.md
137
SECURITY.md
@@ -0,0 +1,137 @@
|
|||||||
|
# Security Model
|
||||||
|
|
||||||
|
## Authentication vs Git Configuration
|
||||||
|
|
||||||
|
### How User Identity Works
|
||||||
|
|
||||||
|
**Question:** Can't I just set my git username to anything and bypass validation?
|
||||||
|
|
||||||
|
**Answer:** No. The validation uses **Gitea account authentication**, not git commit authors.
|
||||||
|
|
||||||
|
#### Git Commit Author (NOT used for validation)
|
||||||
|
```bash
|
||||||
|
# This can be set to anything locally
|
||||||
|
git config user.name "anyone"
|
||||||
|
git config user.email "anyone@example.com"
|
||||||
|
git commit -m "My commit"
|
||||||
|
```
|
||||||
|
These values are **NOT** used for plugin ownership validation.
|
||||||
|
|
||||||
|
#### PR Creator (used for validation)
|
||||||
|
When you create a Pull Request:
|
||||||
|
1. You must be logged into your Gitea account
|
||||||
|
2. The PR is created through Gitea's authenticated API
|
||||||
|
3. `github.event.pull_request.user.login` = your authenticated Gitea username
|
||||||
|
|
||||||
|
**This is what validation checks against.**
|
||||||
|
|
||||||
|
### Example Attack Scenario (Prevented)
|
||||||
|
|
||||||
|
❌ **Attacker tries:**
|
||||||
|
```bash
|
||||||
|
# Configure git to impersonate someone
|
||||||
|
git config user.name "victim"
|
||||||
|
git config user.email "victim@example.com"
|
||||||
|
|
||||||
|
# Modify victim's plugin
|
||||||
|
vim plugins/victim-plugin.json
|
||||||
|
# Change author to "attacker"
|
||||||
|
|
||||||
|
git commit -m "Update plugin"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Create PR (must log into Gitea as "attacker" account)
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **What happens:**
|
||||||
|
1. PR is created by authenticated Gitea user: `attacker`
|
||||||
|
2. Validation script receives: `prAuthor = "attacker"`
|
||||||
|
3. Validation checks original plugin from main branch: `original.author = "victim"`
|
||||||
|
4. Check fails: `"victim" !== "attacker"`
|
||||||
|
5. **Result:** ❌ `Cannot modify plugin owned by "victim" (you are "attacker")`
|
||||||
|
|
||||||
|
The git commit author is irrelevant - what matters is **who is logged into Gitea when creating the PR**.
|
||||||
|
|
||||||
|
## Security Measures
|
||||||
|
|
||||||
|
### 1. Dual Checkout
|
||||||
|
```yaml
|
||||||
|
- Checkout main branch → base/ (trusted validation scripts)
|
||||||
|
- Checkout PR branch → pr/ (untrusted user submissions)
|
||||||
|
```
|
||||||
|
Validation script always runs from `base/`, never from PR branch.
|
||||||
|
|
||||||
|
### 2. Infrastructure Protection
|
||||||
|
PRs that modify `.gitea/` directory are automatically rejected.
|
||||||
|
|
||||||
|
### 3. Ownership Verification
|
||||||
|
All modifications/deletions check **original ownership from main branch**:
|
||||||
|
```javascript
|
||||||
|
// Check original plugin from main (base/)
|
||||||
|
const originalPlugin = readFile('../base/plugins/example.json');
|
||||||
|
|
||||||
|
// User can only modify if they own the original
|
||||||
|
if (originalPlugin.author !== prAuthor) {
|
||||||
|
reject("Cannot modify plugin owned by someone else");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Author Field Validation
|
||||||
|
New plugins must have `author` field matching the PR creator's authenticated Gitea username.
|
||||||
|
|
||||||
|
## What About Collaboration?
|
||||||
|
|
||||||
|
**Q:** What if I want someone else to update my plugin?
|
||||||
|
|
||||||
|
**A:** They cannot submit PRs directly. Options:
|
||||||
|
1. Transfer ownership by updating the `author` field yourself
|
||||||
|
2. They fork your plugin repo and create their own plugin entry
|
||||||
|
3. Add them as collaborators to your plugin repository (not the directory)
|
||||||
|
|
||||||
|
## Trust Model
|
||||||
|
|
||||||
|
### Trusted Components
|
||||||
|
- Main branch content (validated history)
|
||||||
|
- Gitea authentication system
|
||||||
|
- GitHub Actions runner environment
|
||||||
|
- Validation scripts on main branch
|
||||||
|
|
||||||
|
### Untrusted Components
|
||||||
|
- PR branch content (user submissions)
|
||||||
|
- Git commit metadata (names, emails)
|
||||||
|
- PR descriptions and comments
|
||||||
|
|
||||||
|
### Validation Flow
|
||||||
|
```
|
||||||
|
1. User authenticates to Gitea → Trusted user identity
|
||||||
|
2. User creates PR → github.event.pull_request.user.login
|
||||||
|
3. Validation runs from main branch scripts → Trusted code
|
||||||
|
4. Checks plugin ownership against main → Trusted ownership data
|
||||||
|
5. Validates PR author matches plugin author → Secure check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
If you find a security vulnerability in the plugin directory system:
|
||||||
|
1. **Do NOT create a public issue or PR**
|
||||||
|
2. Contact the repository maintainer directly
|
||||||
|
3. Provide detailed reproduction steps
|
||||||
|
4. Allow time for a fix before public disclosure
|
||||||
|
|
||||||
|
## Additional Safeguards
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
Gitea's built-in rate limiting prevents spam PR attacks.
|
||||||
|
|
||||||
|
### Branch Protection
|
||||||
|
The `main` branch is protected:
|
||||||
|
- Direct pushes disabled
|
||||||
|
- All changes via PR
|
||||||
|
- Auto-merge only after validation passes
|
||||||
|
|
||||||
|
### Audit Trail
|
||||||
|
All changes are tracked in git history:
|
||||||
|
- Who created the PR (Gitea account)
|
||||||
|
- What changed (git diff)
|
||||||
|
- When it was merged (git commit timestamp)
|
||||||
|
- Why validation passed (CI logs)
|
||||||
|
|||||||
@@ -14,7 +14,16 @@ All plugin submissions are automatically validated before being merged. PRs must
|
|||||||
- ❌ Cannot modify `.gitea/` infrastructure files
|
- ❌ Cannot modify `.gitea/` infrastructure files
|
||||||
- ⚠️ `plugins/index.json` is auto-generated - don't manually edit it
|
- ⚠️ `plugins/index.json` is auto-generated - don't manually edit it
|
||||||
|
|
||||||
### 2. File Type
|
### 2. File Naming
|
||||||
|
- **Filename must be prefixed with your username**
|
||||||
|
- Format: `username-plugin-name.json`
|
||||||
|
- Example: `litruv-example-plugin.json`
|
||||||
|
- ❌ Invalid: `example-plugin.json` (missing username prefix)
|
||||||
|
- ❌ Invalid: `otheruser-plugin.json` (wrong username)
|
||||||
|
|
||||||
|
This prevents naming conflicts and makes ownership visible.
|
||||||
|
|
||||||
|
### 3. File Type
|
||||||
- ✅ Only `.json` files are allowed in `plugins/` directory
|
- ✅ Only `.json` files are allowed in `plugins/` directory
|
||||||
- ❌ No other file types (images, scripts, etc. must be in plugin repo)
|
- ❌ No other file types (images, scripts, etc. must be in plugin repo)
|
||||||
|
|
||||||
@@ -38,7 +47,8 @@ Every plugin JSON must have these fields:
|
|||||||
|
|
||||||
#### `id` (required)
|
#### `id` (required)
|
||||||
- Must match the filename
|
- Must match the filename
|
||||||
- Example: `example-plugin.json` → `"id": "example-plugin"`
|
- Example: `litruv-example-plugin.json` → `"id": "litruv-example-plugin"`
|
||||||
|
- Must include username prefix matching filename
|
||||||
- Lowercase, hyphens allowed, no spaces
|
- Lowercase, hyphens allowed, no spaces
|
||||||
|
|
||||||
#### `name` (required)
|
#### `name` (required)
|
||||||
@@ -134,9 +144,15 @@ node .gitea/scripts/validate-pr.js "your-username" "plugins/your-plugin.json"
|
|||||||
**Fix**: Change `"author": "someone"` to `"author": "you"`
|
**Fix**: Change `"author": "someone"` to `"author": "you"`
|
||||||
|
|
||||||
### ❌ ID doesn't match filename
|
### ❌ ID doesn't match filename
|
||||||
|
```litruv-my-plugin.json: Plugin ID "different-name" doesn't match filename "litruv-my-plugin.json"
|
||||||
```
|
```
|
||||||
❌ plugins/my-plugin.json: Plugin ID "different-name" doesn't match filename "my-plugin.json"
|
**Fix**: Change `"id": "different-name"` to `"id": "litruv-my-plugin"`
|
||||||
|
|
||||||
|
### ❌ Filename missing username prefix
|
||||||
```
|
```
|
||||||
|
❌ plugins/my-plugin.json: Filename must start with your username "litruv-" (e.g., "litruv-my-plugin.json")
|
||||||
|
```
|
||||||
|
**Fix**: Rename file from `my-plugin.json` to `litruv-my-plugin.json` and update the `id` field
|
||||||
**Fix**: Change `"id": "different-name"` to `"id": "my-plugin"`
|
**Fix**: Change `"id": "different-name"` to `"id": "my-plugin"`
|
||||||
|
|
||||||
### ❌ Invalid version
|
### ❌ Invalid version
|
||||||
@@ -155,13 +171,7 @@ node .gitea/scripts/validate-pr.js "your-username" "plugins/your-plugin.json"
|
|||||||
```
|
```
|
||||||
❌ plugins/example-plugin.json: Thumbnail validation failed: Thumbnail dimensions 1024x768 exceed 512x512
|
❌ plugins/example-plugin.json: Thumbnail validation failed: Thumbnail dimensions 1024x768 exceed 512x512
|
||||||
```
|
```
|
||||||
**Fix**: Resize your thumbnail to 512×512 or smaller
|
**Fix**: litruv-example-plugin",
|
||||||
|
|
||||||
## Example Valid Plugin JSON
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "example-plugin",
|
|
||||||
"name": "Example Plugin",
|
"name": "Example Plugin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "An example plugin demonstrating the plugin system capabilities",
|
"description": "An example plugin demonstrating the plugin system capabilities",
|
||||||
@@ -173,6 +183,14 @@ node .gitea/scripts/validate-pr.js "your-username" "plugins/your-plugin.json"
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Filename: `litruv-example-plugin.json"author": "litruv",
|
||||||
|
"repository": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example.git",
|
||||||
|
"thumbnail": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example/raw/branch/main/thumbnail.png",
|
||||||
|
"homepage": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example",
|
||||||
|
"tags": ["example", "demo"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Security Note
|
## Security Note
|
||||||
|
|
||||||
**Never include sensitive information in plugin JSON files.** These files are public and will be served to all plugin host users. Do not include:
|
**Never include sensitive information in plugin JSON files.** These files are public and will be served to all plugin host users. Do not include:
|
||||||
|
|||||||
Reference in New Issue
Block a user