mirror of
https://github.com/MervinPraison/PraisonAIUI.git
synced 2026-11-07 05:56:17 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2797667e04 | ||
|
|
f23aacdec9 | ||
|
|
8dc8b9f49f |
No files matched your search
+37
@@ -0,0 +1,37 @@
|
||||
# Security Policy
|
||||
|
||||
We take the security of PraisonAIUI seriously and appreciate responsible
|
||||
disclosure of vulnerabilities.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security fixes are applied to the latest released version of PraisonAIUI.
|
||||
Please make sure you are on the most recent release before reporting.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Please do not open a public issue for security vulnerabilities.**
|
||||
|
||||
Report privately through GitHub's **Private Vulnerability Reporting**:
|
||||
|
||||
1. Go to the [Security tab](https://github.com/MervinPraison/PraisonAIUI/security)
|
||||
of this repository.
|
||||
2. Click **"Report a vulnerability"** to open a private advisory.
|
||||
3. Include as much detail as you can:
|
||||
- Affected component(s) and version.
|
||||
- Steps to reproduce.
|
||||
- Impact assessment.
|
||||
- Any suggested fix or mitigation.
|
||||
|
||||
If you are unable to use private reporting, you may open a public issue that
|
||||
contains **no technical specifics** and requests a private channel, and a
|
||||
maintainer will follow up.
|
||||
|
||||
## What to Expect
|
||||
|
||||
- We aim to acknowledge new reports within a few business days.
|
||||
- We will keep you informed as we investigate and work on a fix.
|
||||
- Once a fix is released, we are happy to credit you in the advisory (unless
|
||||
you prefer to remain anonymous).
|
||||
|
||||
Thank you for helping keep PraisonAIUI and its users safe.
|
||||
@@ -44,6 +44,43 @@ def _icon(symbol: str, fallback: str) -> str:
|
||||
return symbol if _supports_unicode() else fallback
|
||||
|
||||
|
||||
def _load_env_files() -> int:
|
||||
"""Load environment variables from the standard PraisonAI ``.env`` files.
|
||||
|
||||
Mirrors the PraisonAI CLI convention so ``env:VAR`` references (e.g. in
|
||||
Channels config) resolve when the server was started without the shell
|
||||
exporting the tokens. Reads ``~/.praisonai/.env`` first, then a ``.env`` in
|
||||
the current working directory. Existing process env vars are never
|
||||
overridden, and parsing is intentionally dependency-free (no python-dotenv).
|
||||
|
||||
Returns the number of variables newly set into ``os.environ``.
|
||||
"""
|
||||
import os
|
||||
|
||||
loaded = 0
|
||||
for env_path in (Path.home() / ".praisonai" / ".env", Path.cwd() / ".env"):
|
||||
try:
|
||||
if not env_path.is_file():
|
||||
continue
|
||||
for line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
if line.startswith("export "):
|
||||
line = line[len("export "):]
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"').strip("'")
|
||||
if key and key not in os.environ:
|
||||
os.environ[key] = value
|
||||
loaded += 1
|
||||
except Exception:
|
||||
# Never let env loading break startup — resolution falls back to
|
||||
# the existing process environment.
|
||||
continue
|
||||
return loaded
|
||||
|
||||
|
||||
app = typer.Typer(
|
||||
name="aiui",
|
||||
help="PraisonAIUI - YAML-driven website generator",
|
||||
@@ -1380,6 +1417,17 @@ def run(
|
||||
console.print(f"[red]Error:[/red] App file not found: {app_file}")
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
# Auto-load standard PraisonAI env files so `env:VAR` references (e.g. in
|
||||
# Channels config) resolve even when the shell did not export the tokens.
|
||||
# This matches the PraisonAI CLI convention and fixes the "Connected but
|
||||
# bot never replies" class of issues caused by empty env-resolved tokens.
|
||||
_loaded = _load_env_files()
|
||||
if _loaded:
|
||||
console.print(
|
||||
f"[green]{_icon('✓', 'OK')}[/green] Loaded {_loaded} environment "
|
||||
f"variable(s) from PraisonAI config"
|
||||
)
|
||||
|
||||
is_yaml = app_file.suffix in (".yaml", ".yml")
|
||||
|
||||
if is_yaml:
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Unit tests for `_load_env_files` — auto-loading PraisonAI `.env` files.
|
||||
|
||||
Regression coverage for issue #286: `env:VAR` channel token references
|
||||
resolved to empty strings because `aiui run` never loaded the standard
|
||||
`~/.praisonai/.env` file, so bots showed "Connected" but never replied.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from praisonaiui.cli import _load_env_files
|
||||
|
||||
|
||||
def _write_env(path, body):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(body, encoding="utf-8")
|
||||
|
||||
|
||||
def test_loads_home_praisonai_env(tmp_path, monkeypatch):
|
||||
"""Variables in ~/.praisonai/.env are loaded into the process env."""
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.delenv("TELEGRAM_BOT_TOKEN", raising=False)
|
||||
|
||||
_write_env(tmp_path / ".praisonai" / ".env", 'TELEGRAM_BOT_TOKEN="123:AAtoken"\n')
|
||||
|
||||
loaded = _load_env_files()
|
||||
|
||||
assert loaded >= 1
|
||||
assert os.environ["TELEGRAM_BOT_TOKEN"] == "123:AAtoken"
|
||||
|
||||
|
||||
def test_does_not_override_existing_env(tmp_path, monkeypatch):
|
||||
"""An already-set process variable is never clobbered by the file."""
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setenv("SLACK_BOT_TOKEN", "xoxb-from-shell")
|
||||
|
||||
_write_env(tmp_path / ".praisonai" / ".env", "SLACK_BOT_TOKEN=xoxb-from-file\n")
|
||||
|
||||
_load_env_files()
|
||||
|
||||
assert os.environ["SLACK_BOT_TOKEN"] == "xoxb-from-shell"
|
||||
|
||||
|
||||
def test_ignores_comments_blanks_and_export_prefix(tmp_path, monkeypatch):
|
||||
"""Comments/blank lines are skipped and `export ` prefixes are stripped."""
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.delenv("FOO", raising=False)
|
||||
monkeypatch.delenv("BAR", raising=False)
|
||||
|
||||
_write_env(
|
||||
tmp_path / ".praisonai" / ".env",
|
||||
"# a comment\n\nexport FOO=foo\nBAR='bar'\n",
|
||||
)
|
||||
|
||||
_load_env_files()
|
||||
|
||||
assert os.environ["FOO"] == "foo"
|
||||
assert os.environ["BAR"] == "bar"
|
||||
|
||||
|
||||
def test_missing_files_are_harmless(tmp_path, monkeypatch):
|
||||
"""No env files present → returns 0 and does not raise."""
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
assert _load_env_files() == 0
|
||||
Reference in new issue
Block a user