fix: sanitize get user response (#3390)
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
Notify HeyPuter / notify (push) Has been cancelled
release-please / release-please (push) Has been cancelled

* fix: sanitize get user response

* fix: app creation in dev center
This commit is contained in:
Daniel Salazar
2026-07-15 02:02:06 -07:00
committed by GitHub
parent 92a6d30e26
commit cf34f9fea9
7 changed files with 229 additions and 17 deletions
@@ -0,0 +1,43 @@
import bcrypt from 'bcrypt';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { PuterServer } from '../../server.ts';
import { setupTestServer } from '../../testUtil.ts';
let server: PuterServer;
beforeAll(async () => {
server = await setupTestServer({
no_default_user: false,
} as never);
});
afterAll(async () => {
await server.shutdown();
});
describe('DefaultUserService — bootstrap admin credentials', () => {
it('stashes the bootstrap password so rotation can be detected', async () => {
const admin = await server.stores.user.getByUsername('admin');
expect(admin).toBeTruthy();
const stashed = admin?.metadata?.tmp_password;
expect(typeof stashed).toBe('string');
expect(
await bcrypt.compare(String(stashed), String(admin?.password)),
).toBe(true);
});
it('drops the plaintext stash once the password is rotated', async () => {
const admin = await server.stores.user.getByUsername('admin');
expect(admin).toBeTruthy();
await server.stores.user.update(admin!.id, {
password: await bcrypt.hash('rotated-password', 8),
});
// Simulate the next boot.
await server.services.defaultUser.onServerStart();
const fresh = await server.stores.user.getByUsername('admin');
expect(fresh?.metadata?.tmp_password ?? null).toBeNull();
});
});
@@ -41,7 +41,8 @@ const ADMIN_STORAGE_BYTES = 10 * 1024 * 1024 * 1024;
*
* Each boot where the current password hash still matches the stashed
* plaintext, the credentials are re-printed to stdout (CI scrapes this
* line to extract the default password).
* line to extract the default password). Once rotation is detected the
* stash is deleted so the plaintext isn't retained longer than needed.
*/
export class DefaultUserService extends PuterService {
override async onServerStart(): Promise<void> {
@@ -88,7 +89,13 @@ export class DefaultUserService extends PuterService {
tmpPassword,
String(user.password),
);
if (!isDefault) return;
if (!isDefault) {
// Password was rotated — stop retaining the plaintext stash.
await this.stores.user.updateMetadata(user.id, {
tmp_password: null,
});
return;
}
this.#printCredentials(tmpPassword);
}