Ogma प्लगइन प्रणाली
Ogma प्लगइन कस्टम बैकएंड लॉजिक, फ्रंटएंड UI पैनल और कार्यप्रवाह के चरण जोड़कर टूल का विस्तार करते हैं। प्लगइन डिस्क की डायरेक्टरी से स्थानीय रूप से इंस्टॉल होते हैं, हर प्रोजेक्ट के लिए अलग से चालू किए जाते हैं और सैंडबॉक्स परिवेश में चलते हैं।
यह दस्तावेज़ प्लगइन डेवलपर के लिए मुख्य संदर्भ है।
सबसे तेज़ शुरुआत के लिए प्लगइन के लिए त्वरित शुरुआत का उपयोग करें।
त्वरित शुरुआत
न्यूनतम बैकएंड प्लगइन
my-plugin/
manifest.json
backend/script.jsmanifest.json:
json
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"plugins": [
{
"kind": "backend",
"id": "my-plugin-backend",
"entrypoint": "backend/script.js"
}
]
}backend/script.js (ES2020; बंडलर का उपयोग करते समय इम्पोर्ट का उपयोग किया जा सकता है):
js
async function init(sdk) {
sdk.console.log("my-plugin started");
sdk.events.onInterceptResponse(function(req, res) {
if (res.getCode() === 403) {
sdk.console.warn("403 on " + req.getUrl());
}
});
}केवल बैकएंड वाला कार्यशील प्लगइन बनाने के लिए इतना पर्याप्त है। इसे backend/script.js में एक ही फ़ाइल के रूप में रखें और manifest.json को सीधे उस फ़ाइल की ओर इंगित करें।
एक मिनट की बिल्ड प्रक्रिया (TypeScript स्रोत)
यदि आप TypeScript में लिख रहे हैं, तो इस संरचना का उपयोग करें:
text
my-plugin/
manifest.json
backend/
src/index.tsबिल्ड:
bash
pnpm add -D @ogmabox/ogma-sdk esbuild typescript
pnpm exec esbuild backend/src/index.ts --bundle --format=iife --platform=neutral --external:@ogma/sdk --external:@ogmabox/ogma-sdk --outfile=backend/script.jsइंस्टॉलेशन: प्लगइन > इंस्टॉल करें, my-plugin/ डायरेक्टरी चुनें। फिर प्लगइन चालू करें।
मैनिफ़ेस्ट संदर्भ
manifest.json पैकेज की रूट डायरेक्टरी में रहता है। सभी फ़ील्ड में बड़े और छोटे अक्षरों का अंतर माना जाता है।
शीर्ष-स्तरीय फ़ील्ड
| फ़ील्ड | आवश्यक | प्रकार | टिप्पणियाँ |
|---|---|---|---|
id | हाँ | स्ट्रिंग | केवल छोटे लैटिन अक्षर, अंक और हाइफ़न। अधिकतम 64 वर्ण। इंस्टॉल किए गए प्लगइन में अद्वितीय। |
version | हाँ | स्ट्रिंग | सिमैंटिक वर्ज़निंग: MAJOR.MINOR.PATCH |
name | नहीं | स्ट्रिंग | UI में दिखाया जाने वाला नाम। डिफ़ॉल्ट id है। |
description | नहीं | स्ट्रिंग | एक पंक्ति का सारांश। |
author | नहीं | ऑब्जेक्ट | { "name": "...", "email": "...", "url": "..." } |
homepage | नहीं | स्ट्रिंग | स्रोत रिपॉज़िटरी या दस्तावेज़ का URL। |
plugins | हाँ | ऐरे | प्लगइन घटक की एक या अधिक एंट्री (नीचे देखें)। |
permissions | नहीं | ऐरे | आवश्यक अनुमति नामों की सूची (अनुमतियाँ देखें)। |
प्लगइन घटक की एंट्री
plugins ऐरे का हर ऑब्जेक्ट एक घटक का विवरण देता है।
बैकएंड घटक:
json
{
"kind": "backend",
"id": "my-plugin-backend",
"entrypoint": "backend/script.js",
"runtime": "javascript",
"assets": "backend/assets"
}फ्रंटएंड घटक:
json
{
"kind": "frontend",
"id": "my-plugin-frontend",
"entrypoint": "frontend/script.js",
"style": "frontend/style.css",
"assets": "frontend/assets",
"backend": { "id": "my-plugin-backend" }
}| फ़ील्ड | आवश्यक | टिप्पणियाँ |
|---|---|---|
kind | हाँ | "backend" या "frontend" |
id | हाँ | मैनिफ़ेस्ट में अद्वितीय। छोटे अक्षर और हाइफ़न। |
entrypoint | हाँ | JS प्रवेश फ़ाइल का सापेक्ष पथ। |
style | नहीं | प्लगइन iframe में लोड होने वाली CSS फ़ाइल। |
assets | नहीं | /plugins/{id}/assets/ के अंतर्गत उपलब्ध कराए जाने वाले स्थिर एसेट की डायरेक्टरी। |
backend.id | नहीं | sdk.backend.* RPC के लिए फ्रंटएंड घटक को उसके बैकएंड घटक से जोड़ता है। |
runtime | नहीं (केवल बैकएंड) | "javascript" (डिफ़ॉल्ट और एकमात्र समर्थित मान)। |
बैकएंड प्लगइन API (sdk)
बैकएंड sdk ऑब्जेक्ट आपके init(sdk) फ़ंक्शन को दिया जाता है। सभी मेथड सिंक्रोनस हैं, जब तक उन्हें async के रूप में चिह्नित न किया गया हो।
sdk.console
js
sdk.console.log("message")
sdk.console.warn("message")
sdk.console.error("message")प्लगइन के लॉग बफ़र में लिखता है (लॉग टैब में दिखाई देता है)। अधिकतम 500 एंट्री रखी जाती हैं। हर संदेश 1 KB पर काट दिया जाता है।
sdk.meta
js
sdk.meta.id() // > string: plugin id (e.g. "my-plugin")
sdk.meta.packageId() // > string: same as id
sdk.meta.version() // > string: semver (e.g. "1.0.0")
sdk.meta.path() // > string: writable data directory for this pluginsdk.meta.path() प्लगइन की लिखने योग्य निजी डेटा डायरेक्टरी की ओर इंगित करता है, जैसे ~/.local/share/ogma/plugins/my-plugin/data। डायरेक्टरी अपने-आप बनती है और रीस्टार्ट के बाद भी प्लगइन डेटा बनाए रखने के लिए इसका उपयोग किया जा सकता है।
स्टेट और फ़ाइल सहायक उपकरणों के लिए sdk.storage, sdk.path और sdk.fs भी उपलब्ध हैं।
sdk.storage
js
sdk.storage.get("key") // > string | null
sdk.storage.set("key", "value")
sdk.storage.delete("key")
sdk.storage.clear()
sdk.storage.keys() // > string[]sdk.storage का दायरा प्लगइन ID तक सीमित है और इसका डेटा प्लगइन के रीस्टार्ट के बाद भी बना रहता है।
sdk.fs
js
sdk.fs.readFile("relative/file.txt") // > string
sdk.fs.writeFile("relative/file.txt", "text")
sdk.fs.appendFile("relative/file.txt", "more")
sdk.fs.exists("relative/file.txt") // > boolean
sdk.fs.existsSync("relative/file.txt") // > boolean
sdk.fs.list("relative/dir") // > string[]
sdk.fs.mkdir("relative/dir")sdk.fs केवल sdk.meta.path() के अंतर्गत आने वाली फ़ाइलों तक सीमित है।
read और write, संगतता के लिए readFile और writeFile के वैकल्पिक नाम बने रहते हैं। ऐसी फ़ाइल बनाने से पहले exists या existsSync का उपयोग करें जिसे आप ओवरराइट नहीं करना चाहते। ये API प्लगइन रनटाइम में सिंक्रोनस रूप से काम करते हैं; ये Node.js का पूरा fs मॉड्यूल नहीं हैं। प्लगइन की फ़ाइल पहुँच के लिए plugin_storage अनुमति आवश्यक है और यह प्लगइन की निजी डेटा डायरेक्टरी के भीतर रहती है। कार्यप्रवाह JavaScript का फ़ाइल सिस्टम संदर्भ अलग है; कार्यप्रवाह की फ़ाइल पहुँच देखें।
sdk.path
js
sdk.path.join("a", "b", "c")
sdk.path.basename("/tmp/file.txt")
sdk.path.dirname("/tmp/file.txt")
sdk.path.extname("file.txt")
sdk.path.resolve("/a", "b")
sdk.path.isAbsolute("/tmp/file.txt")
sdk.path.sepsdk.events
Ogma इवेंट के लिए कॉलबैक रजिस्टर करें। सभी कॉलबैक QuickJS सैंडबॉक्स के भीतर सिंक्रोनस रूप से कॉल किए जाते हैं।
js
sdk.events.onInterceptRequest(function(req) {
// req: RequestSpecRaw
// Return a modified RequestSpecRaw to mutate the request.
// Return null/undefined to pass through unchanged.
});
sdk.events.onInterceptResponse(function(req, res) {
// req: Request (read-only), res: Response (read-only)
// Return value is ignored.
});
sdk.events.onProjectChange(function() {
// no callback args
});
sdk.events.onFindingCreated(function(finding) {
// finding: { id, title, reporter }
});sdk.requests
js
// Get a single HTTP entry by id
var entry = sdk.requests.get("entry-id");
// entry: { id, method, host, path, query, tls, ... } or null
// Search HTTP history
var results = sdk.requests.search({ limit: 20, offset: 0 });
// results: { entries: [...], total: N }
// Send an HTTP request (requires send_requests permission)
var response = await sdk.requests.send(spec);
// spec: RequestSpecRaw (see below)
// response: Responsesdk.requests.send के लिए मैनिफ़ेस्ट में send_requests अनुमति घोषित होना और उपयोगकर्ता द्वारा उसका प्रदान किया जाना आवश्यक है। अनुमतियाँ देखें।
sdk.findings
js
// Create a finding (requires write_findings permission)
sdk.findings.create({
title: "SSRF via redirect",
reporter: "my-plugin",
dedupeKey: "ssrf-" + request.getId(),
request: { id: request.getId() }
});
// Check if a finding already exists (dedup check)
var exists = sdk.findings.exists({ dedupeKey: "ssrf-abc" });
// List findings
var page = sdk.findings.list({ limit: 20, offset: 0 });
// Get a single finding
var finding = sdk.findings.get("finding-id");sdk.findings.create की दर सीमाएँ: प्रति मिनट 10, प्रति प्लगइन सत्र 500, प्रति इवेंट कॉलबैक 3।
sdk.api
ऐसे बैकएंड RPC फ़ंक्शन रजिस्टर करें जिन्हें फ्रंटएंड sdk.backend.* के ज़रिए कॉल कर सके:
js
// In backend init:
sdk.api.register("getScans", function(scanId) {
return { scans: [] };
});
// Emit an event to connected frontends:
sdk.api.send("scan:complete", { scanId: 1, status: "ok" });हैंडलर को फ्रंटएंड से दिए गए आर्ग्युमेंट मिलते हैं (कोई अतिरिक्त sdk आर्ग्युमेंट इंजेक्ट नहीं होता)। लौटाए गए मान JSON में सीरियलाइज़ करके कॉल करने वाले को वापस भेजे जाते हैं।
फ्रंटएंड इन्हें sdk.backend.getScans(scanId) के ज़रिए कॉल करता है - फ्रंटएंड प्लगइन API देखें।
sdk.api.send हर प्लगइन की अपनी कतार में इवेंट भेजता है (अधिकतम 200 एंट्री)। फ्रंटएंड इस कतार को sdk.backend.onEvent के ज़रिए पोल करते हैं।
sdk.replay, sdk.projects, sdk.scope, sdk.workflows, sdk.matchReplace
ये केवल पढ़ने वाली क्वेरी के नेमस्पेस हैं। पूरे मेथड सिग्नेचर के लिए बैकएंड SDK संदर्भ देखें।
अनुरोध क्लास
RequestSpecRaw - इंटरसेप्ट किए गए अनुरोध का प्रतिनिधित्व करता है। यह आपको onInterceptRequest में मिलता है।
js
spec.getMethod() // > string
spec.setMethod("POST")
spec.getHost() // > string
spec.setHost("example.com")
spec.getPort() // > number
spec.getPath() // > string
spec.setPath("/new/path")
spec.getQuery() // > string
spec.getTls() // > boolean
spec.getHeaders() // > Record<string, string[]>
spec.setHeader("X-Foo", "bar")
spec.getBody() // > Body | null
spec.setBody("new body")
spec.getRaw() // > Uint8Array (raw bytes) or []
spec.setRaw(bytes) // set raw bytes
// Create a new spec from a URL string:
var spec = new RequestSpecRaw("https://example.com/path?q=1");Request - कैप्चर किया गया केवल पढ़ने योग्य अनुरोध (sdk.requests.get से)।
js
req.getId()
req.getMethod()
req.getHost()
req.getPort()
req.getTls()
req.getPath()
req.getQuery()
req.getUrl() // > full URL string
req.getHeaders() // > Record<string, string>
req.getHeader("name")
req.getBody() // > Body | null
req.getCreatedAt() // > Date
req.toSpec() // > RequestSpec (mutable copy)Response - कैप्चर की गई केवल पढ़ने योग्य प्रतिक्रिया।
js
res.getCode() // > number (HTTP status)
res.getHeaders() // > Record<string, string>
res.getHeader("name")
res.getBody() // > Body | null
res.getRoundtripTime() // > number (ms)
res.getCreatedAt() // > DateBody:
js
body.toText() // > string
body.toJson() // > parsed object or null
body.toRaw() // > Uint8Array
body.length // > number (original size, may differ from toText() if truncated)फ्रंटएंड प्लगइन API (sdk)
फ्रंटएंड प्लगइन कोड /plugins/{id}/ui से लोड किए गए सैंडबॉक्स iframe में चलता है। iframe, postMessage का उपयोग करके Ogma होस्ट से संवाद करता है, जो कॉल को बैकएंड तक पहुँचाता है।
SDK, window.ogmaSDK के ज़रिए उपलब्ध है। होस्ट ब्रिज स्थापित होने के बाद तैयार SDK प्राप्त करने के लिए ogmaSDK.ready(cb) कॉल करें:
js
ogmaSDK.ready(function(sdk) {
// sdk is the live SDK - safe to call any method here
sdk.log.info("frontend ready");
});सभी SDK मेथड Promise लौटाते हैं।
sdk.log
js
sdk.log.info("message")
sdk.log.warn("message")
sdk.log.error("message")sdk.meta
js
var meta = await sdk.meta.get();
// { pluginId, packageId, name, version, ogmaVersion }sdk.requests
js
var entry = await sdk.requests.get({ id: "entry-id" });
var result = await sdk.requests.search({ limit: 20, offset: 0, query: "host:example.com" });read_http_history अनुमति आवश्यक है (अपने-आप प्रदान की जाती है; उपयोगकर्ता की स्वीकृति आवश्यक नहीं)।
sdk.findings
js
var page = await sdk.findings.list({ limit: 20, offset: 0 });read_findings अनुमति आवश्यक है (अपने-आप प्रदान की जाती है)।
sdk.scope
js
var scope = await sdk.scope.getActive();sdk.projects
js
var project = await sdk.projects.getCurrent();sdk.backend - बैकएंड RPC
बैकएंड पर sdk.api.register से रजिस्टर किए गए फ़ंक्शन कॉल करें:
js
// Call a named backend function
var result = await sdk.backend.call("getScans", [scanId]);
// Poll for backend-emitted events (sdk.api.send on the backend side)
var { events, next_since } = await sdk.backend.poll(since);
// events: [{ event: "scan:complete", args: [...] }]
// Register an event listener (uses polling internally)
sdk.backend.onEvent("scan:complete", function(data) {
console.log("scan done", data);
});sdk.backend.onEvent अंदरूनी रूप से 2 सेकंड की पोलिंग लूप का उपयोग करता है। लौटाए गए अनसब्सक्राइब फ़ंक्शन को कॉल करके सुनना बंद करें:
js
var unsub = sdk.backend.onEvent("scan:complete", handler);
// later:
unsub();sdk.navigation
js
await sdk.navigation.addPage("/my-plugin", { title: "My Plugin" });नेविगेशन पेज रजिस्टर करता है। अभी होस्ट इसकी स्वीकृति देता है। पूरे राउटर एकीकरण पर काम चल रहा है।
sdk.sidebar
js
await sdk.sidebar.registerItem("My Plugin", "/my-plugin", { icon: "puzzle" });साइडबार एंट्री रजिस्टर करता है। अभी यह प्लगइन UI पैनल तक सीमित है - ग्लोबल साइडबार स्लॉट से जोड़ने पर काम चल रहा है।
sdk.commands
js
await sdk.commands.register("my-plugin:scan", {
name: "Scan with My Plugin",
handler: function(context) { /* ... */ }
});होस्ट इसकी स्वीकृति देता है। कमांड पैलेट एकीकरण पर काम चल रहा है।
sdk.menu
js
await sdk.menu.registerItem({
type: "Request",
commandId: "my-plugin:scan",
leadingIcon: "shield"
});होस्ट इसकी स्वीकृति देता है। संदर्भ मेनू में जोड़ने पर काम चल रहा है।
sdk.window
js
sdk.window.showToast("Scan complete", { variant: "success", duration: 3000 });प्लगइन पैनल में टोस्ट सूचना दिखाता है। प्रकार: info, success, warning, error।
sdk.ui
js
sdk.ui.resize(600); // request iframe height change
sdk.ui.sidebar.registerItem("name", "/path"); // alias for sdk.sidebar.registerItemअनुमतियाँ
manifest.json में अनुमतियाँ घोषित करें:
json
{
"permissions": ["send_requests", "write_findings"]
}अपने-आप मिलने वाली अनुमतियाँ (उपयोगकर्ता की स्वीकृति आवश्यक नहीं)
ये अनुमतियाँ हर इंस्टॉल किए गए प्लगइन को हमेशा प्रदान की जाती हैं:
| अनुमति | क्या करने देती है |
|---|---|
read_http_history | sdk.requests.get, sdk.requests.search |
read_findings | sdk.findings.get, sdk.findings.list |
read_scope | sdk.scope.getActive |
read_projects | sdk.projects.getCurrent, sdk.projects.list |
plugin_storage | sdk.storage, sdk.fs, sdk.path |
संरक्षित अनुमतियाँ (उपयोगकर्ता की स्वीकृति आवश्यक)
इन्हें मैनिफ़ेस्ट में घोषित करना और उपयोगकर्ता द्वारा अनुमतियाँ टैब से स्पष्ट रूप से प्रदान करना आवश्यक है:
| अनुमति | क्या करने देती है |
|---|---|
send_requests | sdk.requests.send - बाहरी HTTP अनुरोध भेजना |
write_findings | sdk.findings.create, sdk.findings.update |
संरक्षित अनुमतियाँ घोषित करने वाले प्लगइन को चालू करते समय उपयोगकर्ता को अनुमति माँगने वाला संदेश दिखता है। वे अनुमतियाँ टैब से कभी भी अनुमतियाँ दे या वापस ले सकते हैं।
प्लगइन पैकेज संरचना
प्लगइन पैकेज स्थानीय डायरेक्टरी से इंस्टॉल किया जा सकता है और ब्राउज़र के ज़रिए काम करते समय .zip निर्यात से भी इंस्टॉल किया जा सकता है।
my-plugin/
manifest.json - required
backend/
script.js - bundled backend JS (ES2020)
frontend/
script.js - bundled frontend JS
style.css - optional CSS
assets/ - static assets (images, fonts, etc.)बैकएंड स्क्रिप्ट की आवश्यकताएँ
- एक ही स्व-सम्पूर्ण JS फ़ाइल होनी चाहिए।
require()और डायनेमिकimport()समर्थित नहीं हैं।init(sdk)फ़ंक्शन एक्सपोर्ट करना आवश्यक है (या उसे ग्लोबल के रूप में परिभाषित करें)।- QuickJS द्वारा समर्थित ES2020 उपसमुच्चय:
async/await,Promise,Map,Set,Symbol,Proxy,Date,RegExp,JSON।fetchनहीं,Bufferनहीं। - समर्थित स्टैटिक इम्पोर्ट प्लगइन की प्रीप्रोसेसिंग में हल किए जाते हैं:
@ogma/sdk,crypto,fs,path। - अधिकतम फ़ाइल आकार: 256 KB।
फ्रंटएंड स्क्रिप्ट की आवश्यकताएँ
- सैंडबॉक्स iframe के भीतर चलता है।
connect-src: 'self'की अनुमति है ताकि प्लगइन/plugins/{id}/api/*पर POST कर सके और/plugins/{id}/events/pollको पोल कर सके। - CSP:
default-src 'none'; script-src 'nonce-...'; style-src 'self'; img-src data: blob: 'self'; connect-src 'self'। - iframe सैंडबॉक्स में
allow-same-originनहीं है - प्लगइन Ogma के पैरेंट DOM या कुकी तक नहीं पहुँच सकता। - SDK तक पहुँचने के लिए
ogmaSDK.ready(cb)का उपयोग करें; कॉलबैक चलने से पहले SDK मेथड कॉल न करें।
Ogma के लिए प्लगइन बनाना
बैकएंड एक ही बंडल की गई JS फ़ाइल होना चाहिए, इसलिए इंस्टॉल करने से पहले अपने TypeScript/ES मॉड्यूल स्रोत को बंडल करना आवश्यक है।
अनुशंसित टूलचेन:
bash
# Install dependencies
pnpm install
# Bundle backend (outputs a single CJS/IIFE file):
esbuild packages/backend/src/index.ts \
--bundle \
--platform=neutral \
--format=iife \
--global-name=_plugin \
--outfile=dist/backend/script.js \
--external:@ogma/sdk --external:@ogmabox/ogma-sdk
# Bundle frontend:
vite build packages/frontend --outDir ../../dist/frontendयदि आप Caido डेवलपमेंट टूलचेन (@caido-community/dev) का उपयोग कर रहे हैं, तो caido-dev build चलाएँ और फिर आउटपुट को Ogma-संगत संरचना में कॉपी करें, जिसमें रूट पर manifest.json हो।
प्लगइन इंस्टॉल करना
- बाएँ साइडबार में प्लगइन खोलें।
- इंस्टॉल करें पर क्लिक करें (इंस्टॉल किए गए टैब के ऊपर)।
- डेस्कटॉप ऐप में, नेटिव फ़ोल्डर चयनकर्ता खोलने के लिए ब्राउज़ करें पर क्लिक करें। ब्राउज़र में, प्लगइन डायरेक्टरी का सर्वर पर मौजूद पूरा पथ लिखें।
- मैनिफ़ेस्ट और फ़ाइल सूची जाँचने के लिए मान्यता जाँचें पर क्लिक करें।
- सत्यापन सफल होने पर इंस्टॉल करें पर क्लिक करें।
- सूची में प्लगइन चुनें और चालू करें पर क्लिक करें।
- यदि प्लगइन संरक्षित अनुमतियाँ घोषित करता है, तो उसे चालू करने से पहले अनुमतियाँ टैब से उनकी समीक्षा करके उन्हें प्रदान करें।
समस्या निवारण
प्लगइन का आरंभ बिना त्रुटि संदेश के विफल होता है: लॉग टैब जाँचें। सबसे आम कारण:
sdk.meta.path()कॉल किया गया लेकिन डेटा डायरेक्टरी नहीं बन सकी।init()में हैंडल नहीं किया गया अपवाद।sdk.*कॉल मौजूद नहीं है या गलत लिखा गया है।
फ्रंटएंड खाली दिखता है: CSP उल्लंघन के लिए ब्राउज़र कंसोल जाँचें। सुनिश्चित करें कि आपकी फ्रंटएंड स्क्रिप्ट किसी भी SDK मेथड का उपयोग करने से पहले ogmaSDK.ready(cb) कॉल करती है।
sdk.requests.send, Permission denied त्रुटि देता है: send_requests अनुमति मैनिफ़ेस्ट में घोषित होना और उपयोगकर्ता द्वारा अनुमतियाँ टैब में प्रदान किया जाना, दोनों आवश्यक हैं।
फ्रंटएंड से sdk.api.register फ़ंक्शन कॉल नहीं हो रहे: बैकएंड चालू होना चाहिए (केवल इंस्टॉल किया गया नहीं)। फ़ंक्शन नाम ठीक उसी से मेल खाना चाहिए जो फ्रंटएंड sdk.backend.call को देता है (बड़े और छोटे अक्षरों का अंतर माना जाता है)।
संगतता चेतावनियाँ त्रुटि के रूप में दिखती हैं: ये संचालन नहीं रोकतीं, लेकिन API कवरेज की कमियाँ दर्शाती हैं। API कवरेज के लिए ऊपर दी गई SDK मैपिंग टेबल देखें।