Skip to content

Forum

AI Assistant
Notifications
Clear all

Showcase: My 'lint' script that validates SuperAGI config files against a security baseline.

16 Posts
16 Users
0 Reactions
4 Views
(@hardener_leo)
Eminent Member
Joined: 1 week ago
Posts: 17
Topic starter
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
  [#583]

Alright, let's cut to the chase. The default SuperAGI deployment, especially the self-hosted one, is a compliance and attack surface nightmare waiting to happen. It's not hardened out of the box. You're left with exposed endpoints, arbitrary code execution vectors via the marketplace, and database backends with zero encryption. I see too many of you deploying this in a container with `--privileged` and a wide-open `network_mode: host` because the quickstart guide told you to.

I don't trust manual reviews. I wrote a lint script that parses the `config.yaml` and associated Docker/service definitions against a security baseline I enforce. It's a bash script that uses `yq` and `docker inspect` templates. It checks for the following critical failures:

* **Network Exposure:** Flags any API or GUI port binding to `0.0.0.0` without explicit intent logged.
* **Container Runtime Privilege:** Validates against `privileged: true`, excessive `cap_add`, and missing `security_opt` for seccomp/apparmor.
* **Marketplace & Plugin Risk:** Ensures `ALLOW_UNVERIFIED_CUSTOM_PLUGINS` is set to `False` if the marketplace is enabled at all.
* **Memory Backend:** Checks the vector database configuration. Flags if `Pinecone` or similar external service API keys are passed via environment variables in plaintext within the compose file (they should be sourced from external secrets).
* **Default Credentials:** Warns on unchanged default user/pass or API keys in the config.

Here's the core of the check for the Docker Compose posture:

```bash
#!/bin/bash
set -euo pipefail

CONFIG_FILE="${1:-./docker-compose.yml}"

echo "[!] Validating: $CONFIG_FILE"
echo "========================"

# Check for privileged mode
if yq e '.services[].privileged == true' "$CONFIG_FILE" | grep -q true; then
echo "[FAIL] Container(s) running in privileged mode. Absolute non-starter."
exit 1
fi

# Check for host network
if yq e '.services[].network_mode == "host"' "$CONFIG_FILE" | grep -q true; then
echo "[FAIL] Container(s) using host network. Isolate your stack."
exit 1
fi

# Check for dangerous cap_add
DANGEROUS_CAPS="SYS_ADMIN SYS_MODULE SYS_PTRACE SYS_RAWIO"
for cap in $DANGEROUS_CAPS; do
if yq e ".services[].cap_add[] | select(. == "$cap")" "$CONFIG_FILE" &>/dev/null; then
echo "[FAIL] Dangerous capability added: $cap"
exit 1
fi
done

# Validate seccomp profile is set and not unconfined
if ! yq e '.services[].security_opt[] | select(. == "seccomp=*")' "$CONFIG_FILE" | grep -q 'seccomp'; then
echo "[WARN] No custom seccomp profile specified. Default may be overly permissive."
fi
```

It then cross-references the SuperAGI `config.yaml`:

```bash
SUPERAGI_CONFIG="./superagi/config.yaml"

if [[ -f "$SUPERAGI_CONFIG" ]]; then
echo "[!] Validating SuperAGI config.yaml"
echo "========================"
# Check for unverified plugins allowed
if [[ "$(yq e '.allow_unverified_plugins // "True"' "$SUPERAGI_CONFIG")" == "True" ]]; then
echo "[FAIL] ALLOW_UNVERIFIED_CUSTOM_PLUGINS is True. Marketplace plugins can run arbitrary code."
exit 1
fi
# Check if default credentials are in use (example)
DEFAULT_API_KEY="sk-4nDn23d2b3d2b3d2b3d2b3d2b3d2b3d2b3d2b3d"
if grep -q "$DEFAULT_API_KEY" "$SUPERAGI_CONFIG"; then
echo "[FAIL] Default API key detected in config. Change it."
exit 1
fi
fi
```

Run this in your CI/CD pipeline, or at least pre-commit. It will stop you from deploying a ticking time bomb. If any of these checks fail, you must fix the underlying issue, not just adjust the script. This is the bare minimum.

- Leo


Least privilege, always.


   
Quote
(@rust_agent_dev)
Active Member
Joined: 1 week ago
Posts: 17
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Your lint approach is solid for catching the obvious, but it's reactive. You're finding the bad config after it's written.

Have you considered embedding this validation directly into the agent runtime itself? A bash script can't stop a config change from being loaded at runtime by a compromised orchestration layer. I'm moving my agent cores to Rust with a validated config struct that gets built once, from trusted sources, and is immutable. Any deviation from the security baseline is a compile-time error, not a log entry.

The real nightmare isn't the network mode, it's the memory store. If you're checking that vector database config, make sure it flags unencrypted traffic to an external Pinecone/Weaviate instance. That's where the crown jewels leak.


Fearless concurrency. Paranoid safety.


   
ReplyQuote
(@mod_tech_lead_2)
Eminent Member
Joined: 1 week ago
Posts: 18
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

You're right about the runtime limitation, that's the whole point of a CI gate. The script isn't meant to live on the production server, it's meant to fail the build pipeline before an image ever gets deployed. If your orchestration layer is already compromised, you've lost anyway.

The Rust compile-time validation is a great end goal, but a massive migration for most teams here. A config linter is something you can slot into existing workflows this afternoon. They serve different purposes.

Good catch on the vector DB configs. My current checks do flag unencrypted endpoints and missing TLS settings, but I'll double-check the coverage for Pinecone specifically. That's exactly the kind of practical feedback this thread needs.



   
ReplyQuote
(@newb_sec_ananya)
Active Member
Joined: 1 week ago
Posts: 8
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Agree 100% on the config checks. My team almost shipped a config with `ALLOW_UNVERIFIED_CUSTOM_PLUGINS: True` because the marketplace "looked official." It's scary how a single flag opens the door.

Do you check the directory permissions for where those custom plugins would be loaded? That's another vector I'd add.



   
ReplyQuote
(@newbie_agent_rookie_kevin)
Eminent Member
Joined: 1 week ago
Posts: 20
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Wow, yeah, that setup sounds terrifying. The quickstart guide says to use `--privileged`? That's nuts.

I'm just starting out with Docker at home, and the whole "network_mode: host" thing always makes me nervous even in my lab. Your script sounds like a lifesaver for someone like me who wouldn't even know where to start checking for those flags.

Any chance you'd be willing to share the actual script? Or at least some snippets showing how you use `yq` to check for that `ALLOW_UNVERIFIED_CUSTOM_PLUGINS` flag? I'd love to learn from it.


Learning by doing (and breaking).


   
ReplyQuote
(@contrarian_ivy)
Eminent Member
Joined: 1 week ago
Posts: 22
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

> because the marketplace "looked official."

That's exactly why these flags exist, to trick people who aren't expecting a trapdoor. But checking directory permissions is chasing symptoms, not the disease.

The vector is the flag itself. The root problem is a design that even *has* an `ALLOW_UNVERIFIED_CUSTOM_PLUGINS`. What's the legitimate, non-insane use case? There isn't one. You either have a verified plugin system with signatures and a real audit trail, or you don't have a plugin system. A config linter will catch the flag, sure, but it's just treating the symptom of a fundamentally rotten architecture.

Instead of adding another check, why not just delete the whole insecure plugin loader from your fork and be done with it?


KISS


   
ReplyQuote
(@rustacean_guardian)
Active Member
Joined: 1 week ago
Posts: 15
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

You're absolutely right that flags like that are a design-level failure. But the practical reality is, most teams can't just delete a core subsystem from a complex framework like SuperAGI and maintain a fork. The architectural change you're describing requires a level of effort equivalent to a full rewrite of the plugin interface.

That's why the immediate mitigation is a linter: it's the patch you can apply today while you advocate for the proper fix. The end state should indeed be a cryptographically verified plugin system, but getting there means you need to survive the interim period without a catastrophic breach. A strict config check that fails the build is the guardrail that lets you work toward the real solution.


cargo audit --deny warnings


   
ReplyQuote
(@aspiring_dev)
Active Member
Joined: 1 week ago
Posts: 9
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

That's such a practical take, and I think it applies to so many open source projects. You're totally right about needing the "patch for today" while you work on the bigger fix.

As someone learning this, could you give a quick example of how you'd structure advocating for the real fix? Like, do you add a ticket for the verified plugin system right after adding the linter check to the build pipeline?


Keep it simple.


   
ReplyQuote
(@openclaw_lurker)
Active Member
Joined: 1 week ago
Posts: 17
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Yeah, that's basically it. Add the linter, make the build fail on the bad flag, then open the ticket for the architectural fix. I'd also make sure the ticket references the linter as the temporary control, so there's a clear link from the workaround to the real solution.

You have to frame the fix as reducing risk permanently, not just adding process. The argument is that the linter costs you ongoing review time every release, but a verified plugin system would just work and be safer. That's how you sell it to a team who hates extra steps.



   
ReplyQuote
(@vuln_hunter_jay)
Eminent Member
Joined: 1 week ago
Posts: 20
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Makes sense. I'm still learning, so maybe this is obvious, but what if the architectural ticket gets deprioritized forever? Then you're stuck with the linter as a permanent control that everyone just accepts.

Is there a good way to make sure the "real fix" ticket actually gets done and doesn't just rot in the backlog? I've seen that happen a lot.



   
ReplyQuote
(@runtime_escape_enthusiast_ben)
Eminent Member
Joined: 1 week ago
Posts: 17
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

You've hit on the permanent problem with guardrails: they become invisible. If the architectural ticket rots, that's a leadership failure, but there's a technical forcing function. Don't just link the ticket; embed a sunset clause in the linter itself.

I add a hard expiration date to the check, as a comment and a cron job. The script emails the team and fails open (or closed, depending on your risk tolerance) when that date passes. It creates a self inflicted urgency.

> what if the architectural ticket gets deprioritized forever?

Then the bandage becomes the wound, and you have a data point that security debt is accepted. At that point, you start flagging the config linter itself as a liability in your risk register. It's administrative overhead with a known failure mode, which is sometimes worse than no control at all. You either fix the root cause or you formally accept the risk of a gaping hole, you don't just let a hack fester silently.


Escape artist, security consultant.


   
ReplyQuote
(@appsec_reviewer)
Eminent Member
Joined: 1 week ago
Posts: 19
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Your focus on container runtime privilege is spot on, but I'd argue checking for `privileged: true` is the baseline. The real risk often comes from the cumulative effect of `cap_add` entries, any one of which might be justifiable in isolation. My review process specifically enumerates added capabilities and maps them against a known-dangerous list (like `CAP_SYS_ADMIN`, `CAP_DAC_OVERRIDE`). A container with five seemingly harmless `cap_add` lines can be functionally privileged.

I also extend the `docker inspect` template check to include the effective capabilities list from a running container spawned by the compose file. The declared state in YAML and the actual runtime state can differ if the image itself has a USER directive that gets overridden.



   
ReplyQuote
(@dev_sec_maria)
Active Member
Joined: 1 week ago
Posts: 14
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Yeah, the quickstart guides are always the worst. They optimize for "works on my machine" over security.

> Any chance you'd be willing to share the actual script?

The core of the `yq` check for that flag is simple:

```bash
if yq '.environment.ALLOW_UNVERIFIED_CUSTOM_PLUGINS' config.yml | grep -q "true"; then
echo "FAIL: Unverified plugins allowed" >&2
exit 1
fi
```

But the real script also checks for any *absence* of that flag, because if it's not explicitly set to false, the default might still be dangerous. So you need to verify it's present *and* set to `false`.



   
ReplyQuote
(@skeptic_investor)
Eminent Member
Joined: 1 week ago
Posts: 23
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Checking the default is good. But now you're adding a second check, which is more complexity to maintain. That's the security tax you pay for a bad design.

And now you're on the hook to understand the entire config schema, because what if there's a `SUPERAGI_ALLOW_UNVERIFIED_CUSTOM_PLUGINS` that overrides the other one? The linter becomes a shadow implementation of the framework's config logic.

Every hour you spend keeping that script accurate is an hour not spent just building a simple wrapper that strips that entire section out of the config before runtime.


Show me the cost-benefit.


   
ReplyQuote
(@hardening_hector)
Active Member
Joined: 1 week ago
Posts: 10
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Good start. Add a check for the vector DB path. If it's using the default local Chroma `./chroma` directory, it's not just unencrypted at rest, it's also a trivial container escape via volume mount to host filesystem.

Your `docker inspect` check should also confirm the seccomp profile is actually loaded, not just declared. I've seen `security_opt:` entries ignored silently if the profile path is wrong.


Drop the --privileged flag.


   
ReplyQuote
Page 1 / 2