Hudu Passwords — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Hudu Passwords (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Passwords in Hudu (called "asset passwords" in the API) provide secure credential storage scoped to companies. They allow MSP technicians to store, organize, and retrieve credentials for client infrastructure, applications, and services. Password access can be restricted at the API key level, and all access is logged in Hudu's activity logs.
Critical API naming note: The Hudu UI calls these "Passwords," but the API endpoint is /api/v1/asset_passwords. Always use asset_passwords in API calls.
document the customer's credentials. They are never the connector's own auth: gateway credentials are brokered centrally and are not readable from anywhere in this plugin. An agent that reaches here to "find the API key" has taken a wrong turn.
many MSPs put licence keys and service accounts in asset custom fields. Those are not asset_passwords; use hudu-assets.
this marketplace stores passwords too, with its own permission model. Start from itglue-api-patterns.
skill updates the documented value only. Changing the real password is a tenant or directory operation; use cipp-users or m365-users. Editing the record without changing the system leaves documentation that is confidently wrong.
Passwords are organized by:
Company: Acme Corporation
+-- Passwords
+-- Infrastructure
| +-- Domain Admin - ACME
| +-- Local Admin - Servers
| +-- vCenter Admin
+-- Network
| +-- Firewall Admin
| +-- Switch Admin
| +-- WiFi Controller
+-- Applications
| +-- ERP Admin
| +-- CRM Admin
+-- Cloud Services
+-- Microsoft 365 Global Admin
+-- AWS Root AccountAPI keys in Hudu can be configured to allow or deny password access:
| Permission | Effect |
|---|---|
| Enabled | API key can read/write password values |
| Disabled | API key cannot access password values (403 Forbidden) |
This is configured per API key in Admin > API Keys.
Hudu logs all password access in the activity logs (/api/v1/activity_logs) — who accessed it, when, and what action (view, create, update, delete):
GET /api/v1/activity_logs?resource_type=AssetPassword&resource_id=789Core fields: company_id (required), name (required), username, password, url, description, password_type, otp_secret, password_folder_id.
See references/fields.md for the complete field reference.
| Operation | Request |
|---|---|
| List / filter | GET /api/v1/asset_passwords?company_id=123&name=Domain Admin&page=1 |
| Get one | GET /api/v1/asset_passwords/789 |
| Create | POST /api/v1/asset_passwords with { "asset_password": { ... } } |
| Update | PUT /api/v1/asset_passwords/789 |
| Delete | DELETE /api/v1/asset_passwords/789 (requires DELETE permission) |
GET on a single password returns the plaintext `password` value in the response body. Treat every response from this endpoint as sensitive.
See references/api.md for the complete endpoint catalog with request/response examples.
Never include actual password values in:
When displaying password information, always mask the actual value:
Password: Domain Admin - ACME
Username: [email protected]
Password: **************
URL: https://dc01.acme.localasync function createSecurePassword(companyId, data) {
const password = await createAssetPassword({
company_id: companyId,
name: data.name,
username: data.username,
password: data.password,
url: data.url,
description: `Created: ${new Date().toLocaleDateString()}\nPurpose: ${data.purpose}`,
password_type: data.type,
password_folder_id: data.folderId
});
return password;
}Hudu keeps no rotation history of its own — append rotation dates to description so the audit trail survives.
async function rotatePassword(passwordId, newPassword, reason) {
// Get current password info (for logging, not the value)
const current = await getAssetPassword(passwordId);
// Update with new password
const updated = await updateAssetPassword(passwordId, {
password: newPassword,
description: `${current.description || ''}\nRotated: ${new Date().toLocaleDateString()} - ${reason}`
});
return updated;
}The API filters only on name and company_id; matching against description or URL requires a client-side pass.
async function findPasswordsForServer(companyId, serverName) {
const passwords = await fetchAssetPasswords({ company_id: companyId });
return passwords.filter(p =>
p.name.toLowerCase().includes(serverName.toLowerCase()) ||
p.description?.toLowerCase().includes(serverName.toLowerCase()) ||
p.url?.toLowerCase().includes(serverName.toLowerCase())
);
}async function findStalePasswords(companyId, daysOld = 90) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
const passwords = await fetchAssetPasswords({ company_id: companyId });
return passwords
.filter(p => new Date(p.updated_at) < cutoffDate)
.map(p => ({
id: p.id,
name: p.name,
username: p.username,
lastUpdated: p.updated_at,
daysSinceUpdate: Math.floor(
(new Date() - new Date(p.updated_at)) / (1000 * 60 * 60 * 24)
)
}));
}async function generatePasswordReport(companyId) {
const passwords = await fetchAssetPasswords({ company_id: companyId });
const byType = {};
passwords.forEach(p => {
const type = p.password_type || 'Uncategorized';
if (!byType[type]) byType[type] = [];
byType[type].push({
name: p.name,
username: p.username,
url: p.url,
lastUpdated: p.updated_at
// NEVER include actual password values in reports
});
});
return byType;
}/api/v1/passwords does not exist.company_id rather than sweeping the tenant.See references/errors.md for the complete error and validation table plus a secure error-handling pattern.
otp_secret field~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.