docs: Update Network Policy findings - OPA/Rego UI editor, not API accessible
- Network Policy exists as Rego editor in console UI - cumin.dev/network-policy returns 405 (endpoint exists) not 404 - Not accessible via MCP or bearer token - UI only feature - Rating updated: 2/10 -> 7/10
هذا الالتزام موجود في:
@@ -436,13 +436,36 @@ await callTool("update_constellation", {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 5.8 Network Policy
|
### 5.8 Network Policy (OPA/Rego)
|
||||||
|
|
||||||
**Rating: ⭐ 2/10**
|
**Rating: ⭐⭐⭐⭐ 7/10 — UI Only**
|
||||||
|
|
||||||
Expected to allow ingress/egress rules, rate limiting, and IP allowlisting.
|
Network Policy in Cumin is implemented as an **OPA (Open Policy Agent) Rego policy editor**, visible in the console sidebar. It uses Rego syntax to define allowed ingress/egress rules for the namespace mesh.
|
||||||
|
|
||||||
**Result:** `404 Not Found` — feature either in beta or undocumented for standard tokens.
|
**The default policy loaded in the UI:**
|
||||||
|
```rego
|
||||||
|
package runtime
|
||||||
|
import rego.v1
|
||||||
|
default allow := false
|
||||||
|
allow if true
|
||||||
|
default group_ingress := false
|
||||||
|
group_ingress if true
|
||||||
|
egress_allow_cidr contains "0.0.0.0/0"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Endpoint discovery results:**
|
||||||
|
|
||||||
|
| Domain | Method | Path | Response | Conclusion |
|
||||||
|
|--------|--------|------|----------|------------|
|
||||||
|
| `api.cumin.dev` | ALL | `/network-policy` | 404 | Not on API domain |
|
||||||
|
| `cumin.dev` | GET | `/network-policy` | 404 | No GET handler |
|
||||||
|
| `cumin.dev` | PUT/PATCH | `/network-policy` | **405** | **Endpoint exists!** |
|
||||||
|
| MCP | — | `list_network_policies` | tool not found | Not in MCP tools |
|
||||||
|
|
||||||
|
**Conclusion:** The Network Policy endpoint lives on `cumin.dev` (not `api.cumin.dev`) and returns **405 Method Not Allowed** for PUT/PATCH — meaning the route is registered by nginx but handled differently (likely via a session cookie from the console UI, not a bearer token). It is currently a **UI-only feature** not accessible via the standard MCP/bearer-token API.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This is consistent with the feature being an account-level control plane setting, not a per-project data plane setting. To configure Network Policy, use the Cumin Console sidebar: `api.cumin.dev/console#/network-policy`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -836,7 +859,7 @@ xychart-beta
|
|||||||
| 🪣 S3 Buckets | **8.5/10** | S3-compatible, instant setup |
|
| 🪣 S3 Buckets | **8.5/10** | S3-compatible, instant setup |
|
||||||
| 🔐 Secrets | **9/10** | ✅ Works — value must be base64, project_id required |
|
| 🔐 Secrets | **9/10** | ✅ Works — value must be base64, project_id required |
|
||||||
| 🌐 Constellations | **9.5/10** | ✅ Works — creates private net + shared endpoint |
|
| 🌐 Constellations | **9.5/10** | ✅ Works — creates private net + shared endpoint |
|
||||||
| 🔒 Network Policy | **2/10** | Tool not found in MCP tools list |
|
| 🔒 Network Policy | **7/10** | ⚠️ UI only (OPA/Rego editor) — no MCP/API access |
|
||||||
| 🔑 Pull Secrets | **8/10** | ✅ Works — validates real registry credentials live |
|
| 🔑 Pull Secrets | **8/10** | ✅ Works — validates real registry credentials live |
|
||||||
| 📖 Documentation | **6/10** | Good for basics, sparse on advanced features |
|
| 📖 Documentation | **6/10** | Good for basics, sparse on advanced features |
|
||||||
| 💻 Developer Experience | **9.5/10** | Clean UI, great DX, all core features accessible |
|
| 💻 Developer Experience | **9.5/10** | Clean UI, great DX, all core features accessible |
|
||||||
|
|||||||
@@ -1,127 +1,44 @@
|
|||||||
// Full advanced features test + integration with SOC system
|
|
||||||
const TOKEN = "cumin_GjynCIFJtyoZ_73wasCoWNYf7Y-Pk0jMffHEdRzblBg";
|
const TOKEN = "cumin_GjynCIFJtyoZ_73wasCoWNYf7Y-Pk0jMffHEdRzblBg";
|
||||||
const PROJECT_ID = "178bfad9-5edc-409f-833c-6fffca7aed5a";
|
|
||||||
const API = "https://api.cumin.dev";
|
|
||||||
let SESSION_ID = null;
|
|
||||||
|
|
||||||
async function mcpRequest(method, params, id) {
|
const policy = ["package runtime", "import rego.v1", "default allow := false",
|
||||||
const body = { jsonrpc: "2.0", method, id };
|
"allow if true", "default group_ingress := false", "group_ingress if true",
|
||||||
if (params) body.params = params;
|
'egress_allow_cidr contains "0.0.0.0/0"'].join("\n");
|
||||||
const headers = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", Accept: "application/json, text/event-stream" };
|
|
||||||
if (SESSION_ID) headers["Mcp-Session-Id"] = SESSION_ID;
|
|
||||||
const res = await fetch(`${API}/mcp`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
||||||
const sid = res.headers.get("Mcp-Session-Id");
|
|
||||||
if (sid) SESSION_ID = sid;
|
|
||||||
const data = await res.json();
|
|
||||||
if (data.error) throw new Error(`MCP error: ${JSON.stringify(data.error)}`);
|
|
||||||
return data.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function callTool(name, args) {
|
async function t(method, url, body) {
|
||||||
const r = await mcpRequest("tools/call", { name, arguments: args }, Date.now());
|
const opts = { method, headers: { Authorization: "Bearer " + TOKEN, "Content-Type": "application/json" } };
|
||||||
const text = r.content?.map(c => c.text || "").join("") || JSON.stringify(r);
|
if (body) opts.body = JSON.stringify(body);
|
||||||
if (r.isError) return { error: text };
|
try {
|
||||||
try { return JSON.parse(text); } catch { return text; }
|
const r = await fetch(url, opts);
|
||||||
|
const text = await r.text();
|
||||||
|
const hit = r.status !== 404;
|
||||||
|
console.log(hit ? "💡 HIT!" : " ", method.padEnd(6), url.replace("https://cumin.dev","").padEnd(30), "->", r.status, ":", text.substring(0, 200));
|
||||||
|
return { status: r.status, text };
|
||||||
|
} catch(e) { console.log(" ERR", method, url, e.message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log("═══ ADVANCED FEATURES - FULL INTEGRATION ═══\n");
|
console.log("=== Network Policy — 405 means endpoint EXISTS, wrong method ===\n");
|
||||||
|
|
||||||
await mcpRequest("initialize", { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "soc-features", version: "2.0" } }, 1);
|
// cumin.dev (not api.cumin.dev) returns 405 for PUT — endpoint exists!
|
||||||
console.log("✅ MCP Session:", SESSION_ID, "\n");
|
// Try GET and PATCH on the same paths
|
||||||
|
const paths = ["/network-policy", "/api/network-policy", "/v1/network-policy", "/network-policies", "/v1/network-policies"];
|
||||||
|
|
||||||
// List all available tools first
|
for (const path of paths) {
|
||||||
console.log("━━━ AVAILABLE MCP TOOLS ━━━");
|
await t("GET", "https://cumin.dev" + path);
|
||||||
const tools = await mcpRequest("tools/list", {}, Date.now());
|
await t("PATCH", "https://cumin.dev" + path, { policy });
|
||||||
if (tools?.tools) {
|
await t("POST", "https://cumin.dev" + path, { policy });
|
||||||
tools.tools.forEach(t => console.log(" -", t.name));
|
await t("POST", "https://cumin.dev" + path, { rego: policy });
|
||||||
}
|
await t("PATCH", "https://cumin.dev" + path, { rego: policy });
|
||||||
console.log();
|
console.log("");
|
||||||
|
|
||||||
// ═══ 1. SECRETS ═══
|
|
||||||
console.log("━━━ 1. SECRETS ━━━");
|
|
||||||
|
|
||||||
const secretsList = await callTool("list_secrets", { project_id: PROJECT_ID });
|
|
||||||
console.log(" Existing secrets:", JSON.stringify(secretsList));
|
|
||||||
|
|
||||||
// Value MUST be base64 encoded
|
|
||||||
const secrets = [
|
|
||||||
{ name: "soc-api-key", value: Buffer.from("soc-api-key-2026-prod").toString("base64") },
|
|
||||||
{ name: "soc-threat-intel-token", value: Buffer.from("threat-intel-feed-token-xyz").toString("base64") },
|
|
||||||
{ name: "soc-db-password", value: Buffer.from("SOC_DB_P@ssw0rd!2026").toString("base64") },
|
|
||||||
];
|
|
||||||
|
|
||||||
const createdSecrets = [];
|
|
||||||
for (const s of secrets) {
|
|
||||||
const r = await callTool("create_secret", { project_id: PROJECT_ID, name: s.name, value: s.value });
|
|
||||||
if (r.error) {
|
|
||||||
console.log(` ⚠️ ${s.name}: ${r.error}`);
|
|
||||||
} else {
|
|
||||||
console.log(` ✅ Created secret: ${s.name} → ID: ${r.id || JSON.stringify(r)}`);
|
|
||||||
createdSecrets.push({ name: s.name, id: r.id });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══ 2. CONSTELLATIONS ═══
|
// Also try with different payload keys
|
||||||
console.log("\n━━━ 2. CONSTELLATIONS ━━━");
|
console.log("=== Testing different payload keys on GET /network-policy ===");
|
||||||
|
const url = "https://cumin.dev/network-policy";
|
||||||
const constList = await callTool("list_constellations", { project_id: PROJECT_ID });
|
await t("GET", url);
|
||||||
console.log(" Existing constellations:", JSON.stringify(constList));
|
await t("PATCH", url, { network_policy: policy });
|
||||||
|
await t("PATCH", url, { data: policy });
|
||||||
// Create SOC private network
|
await t("PUT", url, { rego: policy });
|
||||||
const constResult = await callTool("create_constellation", {
|
|
||||||
project_id: PROJECT_ID,
|
|
||||||
name: "soc-private-network"
|
|
||||||
});
|
|
||||||
console.log(" ✅ Created constellation:", JSON.stringify(constResult));
|
|
||||||
const constId = constResult.id || constResult;
|
|
||||||
|
|
||||||
// Try to add our apps to the constellation
|
|
||||||
const appsList = await callTool("list_apps", { project_id: PROJECT_ID });
|
|
||||||
const apps = typeof appsList === 'string' ? JSON.parse(appsList) : appsList;
|
|
||||||
console.log("\n Apps to add to constellation:");
|
|
||||||
apps.forEach(a => console.log(` - ${a.name}: ${a.id} (${a.status})`));
|
|
||||||
|
|
||||||
// Try adding apps to constellation
|
|
||||||
for (const app of apps) {
|
|
||||||
const addResult = await callTool("add_app_to_constellation", {
|
|
||||||
project_id: PROJECT_ID,
|
|
||||||
constellation_id: constId,
|
|
||||||
app_id: app.id
|
|
||||||
});
|
|
||||||
if (addResult && addResult.error) {
|
|
||||||
console.log(` ⚠️ add ${app.name}: ${addResult.error}`);
|
|
||||||
} else {
|
|
||||||
console.log(` ✅ Added ${app.name} to constellation`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ═══ 3. PULL SECRETS ═══
|
|
||||||
console.log("\n━━━ 3. PULL SECRETS ━━━");
|
|
||||||
|
|
||||||
const pullList = await callTool("list_pull_secrets", { project_id: PROJECT_ID });
|
|
||||||
console.log(" Existing pull secrets:", JSON.stringify(pullList));
|
|
||||||
|
|
||||||
const createPull = await callTool("create_pull_secret", {
|
|
||||||
project_id: PROJECT_ID,
|
|
||||||
name: "soc-ghcr",
|
|
||||||
server: "ghcr.io",
|
|
||||||
username: "soc-deployer",
|
|
||||||
password: Buffer.from("ghp_placeholder_token").toString("base64")
|
|
||||||
});
|
|
||||||
console.log(" create_pull_secret:", JSON.stringify(createPull));
|
|
||||||
|
|
||||||
// ═══ FINAL STATE ═══
|
|
||||||
console.log("\n━━━ FINAL STATE ━━━");
|
|
||||||
|
|
||||||
const finalSecrets = await callTool("list_secrets", { project_id: PROJECT_ID });
|
|
||||||
console.log(" Secrets:", JSON.stringify(finalSecrets));
|
|
||||||
|
|
||||||
const finalConst = await callTool("list_constellations", { project_id: PROJECT_ID });
|
|
||||||
console.log(" Constellations:", JSON.stringify(finalConst));
|
|
||||||
|
|
||||||
const finalPull = await callTool("list_pull_secrets", { project_id: PROJECT_ID });
|
|
||||||
console.log(" Pull Secrets:", JSON.stringify(finalPull));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(e => console.error("Fatal:", e.message));
|
main();
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم