diff --git a/SCHEMA.md b/SCHEMA.md deleted file mode 100644 index 4edcdbc..0000000 --- a/SCHEMA.md +++ /dev/null @@ -1,73 +0,0 @@ -# Plugin Schema - -All plugin definitions must be valid JSON files following this schema. - -## Required Fields - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `id` | string | Unique identifier (must match filename) | `"awesome-plugin"` | -| `name` | string | Display name | `"Awesome Plugin"` | -| `version` | string | Semantic version | `"1.0.0"` | -| `description` | string | What the plugin does | `"Does awesome things"` | -| `author` | string | Gitea username of creator | `"litruv"` | -| `repository` | string | Git repository URL | `"http://synbox.ruv.wtf:8418/litruv/awesome-plugin.git"` | - -## Optional Fields - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `downloadUrl` | string | Direct download URL | `"http://synbox.ruv.wtf:8418/litruv/awesome-plugin/archive/main.zip"` | -| `homepage` | string | Plugin homepage or documentation | `"http://synbox.ruv.wtf:8418/litruv/awesome-plugin"` | -| `tags` | array | Category tags | `["utility", "automation"]` | -| `addedDate` | string | ISO 8601 date | `"2026-04-17T00:00:00.000Z"` | -| `dependencies` | object | Required dependencies | `{"lodash": "^4.17.21"}` | - -## Example - -```json -{ - "id": "example-plugin", - "name": "Example Plugin", - "version": "1.0.0", - "description": "An example plugin demonstrating the plugin system capabilities", - "author": "litruv", - "repository": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example.git", - "downloadUrl": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example/archive/main.zip", - "homepage": "http://synbox.ruv.wtf:8418/litruv/Plugin-Example", - "tags": ["example", "demo"], - "addedDate": "2026-04-17T00:00:00.000Z" -} -``` - -## Validation Rules - -1. **Filename Match**: Plugin ID must match the filename (without .json extension) - - ✅ `awesome-plugin.json` with `"id": "awesome-plugin"` - - ❌ `awesome-plugin.json` with `"id": "different-name"` - -2. **Version Format**: Must follow semantic versioning (X.Y.Z) - - ✅ `"1.0.0"`, `"2.5.3"`, `"0.1.0"` - - ❌ `"1.0"`, `"v1.0.0"`, `"latest"` - -3. **Author Matching**: For new plugins, author must match PR creator - - If PR is by user `litruv`, author field must be `"litruv"` - -4. **Valid URLs**: Repository, downloadUrl, and homepage must be valid URLs - - ✅ `"http://example.com/repo.git"` - - ❌ `"not-a-url"`, `"//example.com"` - -5. **Tags**: If present, must be an array of strings - - ✅ `["tag1", "tag2"]` - - ❌ `"tag1,tag2"`, `{"tag": "value"}` - -## Submission Process - -1. Create a JSON file in the `plugins/` directory -2. Filename must be `{plugin-id}.json` -3. Ensure all required fields are present -4. Author field must match your Gitea username -5. Create a Pull Request -6. Automated validation will run -7. If valid, PR auto-merges -8. If invalid, you'll receive comments explaining what to fix diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index c11a3cb..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,137 +0,0 @@ -# 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) diff --git a/VALIDATION_RULES.md b/VALIDATION_RULES.md deleted file mode 100644 index af97ca3..0000000 --- a/VALIDATION_RULES.md +++ /dev/null @@ -1,200 +0,0 @@ -# Plugin Validation Rules - -All plugin submissions are automatically validated before being merged. PRs must pass all validation checks to be auto-merged. - -## Security - -**The validation script runs from the main branch, not from your PR branch.** This prevents malicious PRs from modifying the validation logic. PRs that attempt to modify `.gitea/` directory files will be automatically rejected. - -## File Requirements - -### 1. Location -- ✅ Only modify files in `plugins/` directory -- ❌ Cannot modify files outside `plugins/` -- ❌ Cannot modify `.gitea/` infrastructure files -- ⚠️ `plugins/index.json` is auto-generated - don't manually edit it - -### 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 -- ❌ No other file types (images, scripts, etc. must be in plugin repo) - -## Plugin JSON Schema - -### Required Fields -Every plugin JSON must have these fields: - -```json -{ - "id": "string", - "name": "string", - "version": "string", - "description": "string", - "author": "string", - "repository": "string" -} -``` - -### Field Validation Rules - -#### `id` (required) -- Must match the filename -- Example: `litruv-example-plugin.json` → `"id": "litruv-example-plugin"` -- Must include username prefix matching filename -- Lowercase, hyphens allowed, no spaces - -#### `name` (required) -- Display name for the plugin -- Human-readable, any format - -#### `version` (required) -- Must follow semantic versioning: `x.y.z` -- Example: `"1.0.0"`, `"2.1.3"` -- ❌ Invalid: `"v1.0"`, `"1.0"`, `"latest"` - -#### `description` (required) -- Short description of what the plugin does -- Will be shown in the plugin browser - -#### `author` (required) -- **MUST MATCH YOUR GITEA USERNAME** -- This enforces ownership - you can only submit/remove plugins authored by you -- Case-sensitive - -#### `repository` (required) -- Git repository URL where the plugin code lives -- Must be a valid URL -- Example: `"http://synbox.ruv.wtf:8418/username/Plugin-Name.git"` - -#### `thumbnail` (optional) -- URL to plugin thumbnail image -- **Requirements:** - - Format: PNG, JPG, or GIF only - - Max dimensions: 512×512 pixels - - Max file size: 2MB - - Must be accessible via HTTP/HTTPS -- Example: `"http://synbox.ruv.wtf:8418/username/Plugin-Name/raw/branch/main/thumbnail.png"` -- Validation checks actual image format (magic bytes), dimensions, and size - -### Optional Fields -You can include any additional fields for metadata: -- `homepage` - Project homepage URL -- `downloadUrl` - Direct download link -- `tags` - Array of tags -- `addedDate` - ISO timestamp - -## Ownership Rules - -### Adding Plugins -- The `author` field must match your Gitea username -- This is checked automatically - -### Modifying Plugins -- **You can only modify plugins where the ORIGINAL author (on main branch) is you** -- Even if you change the `author` field in your PR, validation checks against the original -- Attempting to modify someone else's plugin will fail validation with: - ``` - ❌ plugins/their-plugin.json: Cannot modify plugin owned by "them" (you are "you") - ``` - -### Removing Plugins -- **You can only remove plugins where the ORIGINAL author (on main branch) is you** -- The validation checks the plugin's author from the main branch, not from your PR -- Attempting to remove someone else's plugin will fail validation with: - ``` - ❌ plugins/their-plugin.json: Cannot remove plugin owned by "them" (you are "you") - ``` - -**Security Note:** All ownership checks use the plugin data from the `main` branch, not from your PR. You cannot bypass ownership by modifying the `author` field. - -## Validation Process - -1. **File check**: Ensures only `plugins/*.json` files are modified -2. **Infrastructure check**: Blocks any `.gitea/` modifications -3. **JSON parsing**: Validates JSON syntax -4. **Schema validation**: Checks all required fields exist -5. **Field validation**: Validates each field's format and rules -6. **Thumbnail validation** (if provided): Downloads and validates image -7. **Author matching**: Ensures `author` field matches PR creator -8. **Auto-merge**: If all checks pass, PR is automatically merged - -## Testing Locally - -You can test your plugin JSON before submitting: - -```bash -cd Plugin-Directory -node .gitea/scripts/validate-pr.js "your-username" "plugins/your-plugin.json" -``` - -## Common Errors - -### ❌ Author mismatch -``` -❌ plugins/example-plugin.json: Author "someone" must match PR creator "you" -``` -**Fix**: Change `"author": "someone"` to `"author": "you"` - -### ❌ ID doesn't match filename -```litruv-my-plugin.json: Plugin ID "different-name" doesn't match filename "litruv-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"` - -### ❌ Invalid version -``` -❌ plugins/example-plugin.json: Invalid version format: v1.0 -``` -**Fix**: Use semantic versioning like `"version": "1.0.0"` - -### ❌ Thumbnail too large -``` -❌ plugins/example-plugin.json: Thumbnail validation failed: Thumbnail exceeds 2MB limit (3.45MB) -``` -**Fix**: Compress your thumbnail to under 2MB - -### ❌ Thumbnail dimensions too large -``` -❌ plugins/example-plugin.json: Thumbnail validation failed: Thumbnail dimensions 1024x768 exceed 512x512 -``` -**Fix**: litruv-example-plugin", - "name": "Example Plugin", - "version": "1.0.0", - "description": "An example plugin demonstrating the plugin system capabilities", - "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"] -} -``` - -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 - -**Never include sensitive information in plugin JSON files.** These files are public and will be served to all plugin host users. Do not include: -- API keys or tokens -- Passwords -- Private URLs or endpoints -- Personal information