Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ DEL_INSTANCE=false

# Provider: postgresql | mysql | psql_bouncer
DATABASE_PROVIDER=postgresql
DATABASE_CONNECTION_URI='postgresql://user:pass@evolution-postgres:5432/evolution_db?schema=evolution_api'
DATABASE_CONNECTION_URI=postgresql://user:pass@evolution-postgres:5432/evolution_db

# Postgres container settings (used by docker-compose)
POSTGRES_DATABASE=evolution_db
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ COPY ./prisma ./prisma
COPY ./manager ./manager
COPY ./.env.example ./.env
COPY ./runWithProvider.js ./
COPY ./prisma.config.ts ./

COPY ./Docker ./Docker

Expand Down Expand Up @@ -65,6 +66,7 @@ COPY --from=builder /evolution/.env ./.env
COPY --from=builder /evolution/Docker ./Docker
COPY --from=builder /evolution/runWithProvider.js ./runWithProvider.js
COPY --from=builder /evolution/tsup.config.ts ./tsup.config.ts
COPY --from=builder /evolution/prisma.config.ts ./prisma.config.ts

ENV DOCKER_ENV=true

Expand Down
1 change: 1 addition & 0 deletions patches/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

7 changes: 6 additions & 1 deletion src/api/abstract/abstract.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export abstract class RouterBroker {
}

if (request.originalUrl.includes('/instance/create')) {
Object.assign(instance, sanitizeUntrustedInput(body));
const sanitized = sanitizeUntrustedInput(body);
// instanceName must come from the body on create — there is no URL param on this route
if (body?.instanceName !== undefined) {
sanitized.instanceName = body.instanceName;
}
Object.assign(instance, sanitized);
Comment on lines +54 to +59

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Overriding instanceName after sanitization may reintroduce unsafe data from the raw body.

By setting sanitized.instanceName = body.instanceName after calling sanitizeUntrustedInput, you circumvent any validation or filtering that function would apply to instanceName. If sanitizeUntrustedInput is responsible for enforcing allowed keys or validating value formats, consider either updating it to explicitly handle instanceName or adding explicit validation here before assigning, rather than copying the raw value.

}

Object.assign(ref, body);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class EvoHubControlPlaneRouter extends RouterBroker {
});

this.router.get('/evohub/channels/:id', guard, async (req, res) => {
res.json(await evoHubClient.getChannel(req.params.id));
res.json(await evoHubClient.getChannel(req.params.id as string));
});

this.router.get('/evohub/available-channels', guard, async (_req, res) => {
Expand Down Expand Up @@ -114,7 +114,7 @@ export class EvoHubControlPlaneRouter extends RouterBroker {
});

this.router.post('/evohub/channels/:id/meta-connect', guard, async (req, res) => {
res.json(await evoHubClient.connectToMeta(req.params.id, req.body));
res.json(await evoHubClient.connectToMeta(req.params.id as string, req.body));
});
}
}
17 changes: 17 additions & 0 deletions src/api/services/monitor.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,25 @@ export class WAMonitoringService {
businessId: data.businessId,
},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider wrapping instance creation and Setting upsert in a single transaction for atomicity.

If the Instance is created but the Setting upsert fails (e.g., transient DB error), you can still end up with an Instance without a Setting, which undermines the intent of this change. Wrapping both operations in this.prismaRepository.$transaction would keep them atomic and prevent this partial state.

Suggested implementation:

          businessId: data.businessId,
        },
      });
      await this.prismaRepository.$transaction(async (tx) => {
        // Ensure Instance creation and Setting record creation happen atomically.
        await tx.instance.create({
          data: {
            businessId: data.businessId,
          },
        });

        // Ensure Setting record exists immediately after Instance creation.
        // Prevents FK constraint violations (Setting_instanceId_fkey) in chatbot
        // integrations that write to IntegrationSession before setSettings() runs.
        await tx.setting.upsert({
          where: { instanceId: data.instanceId },
          update: {},
          create: {
            instanceId: data.instanceId,
            rejectCall: false,
            groupsIgnore: false,
            alwaysOnline: false,
            readMessages: false,
            readStatus: false,
            syncFullHistory: false,
          },
        });
      });

The tx.instance.create block in the replacement currently only includes businessId: data.businessId because only that part of the original data object was visible in the snippet. To avoid changing behavior, the full data payload used in the original this.prismaRepository.instance.create({ data: { ... } }) call must be copied into the new tx.instance.create({ data: { ... } }) block, preserving all fields that were previously set (e.g., any other properties before businessId).

// Ensure Setting record exists immediately after Instance creation.
// Prevents FK constraint violations (Setting_instanceId_fkey) in chatbot
// integrations that write to IntegrationSession before setSettings() runs.
await this.prismaRepository.setting.upsert({
where: { instanceId: data.instanceId },
update: {},
create: {
instanceId: data.instanceId,
rejectCall: false,
groupsIgnore: false,
alwaysOnline: false,
readMessages: false,
readStatus: false,
syncFullHistory: false,
},
});
} catch (error) {
this.logger.error(error);
throw error;
}
}

Expand Down
7 changes: 4 additions & 3 deletions tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ export default defineConfig({
// Apenas o nome exato @prisma/client é bundlado (redirecionado pelo alias para o client
// gerado). Subpaths como @prisma/client/runtime/* permanecem externos (node_modules).
noExternal: [/^@prisma\/client$/],
define: {
__LICENSE_ENDPOINT_ENCODED__: licenseEndpointEncoded,
__LICENSE_ENDPOINT_XOR_KEY__: licenseEndpointXorKey,
},
esbuildOptions(options) {
// platform node: garante o shim de import.meta.url no bundle CJS (client Prisma 7 usa import.meta)
options.platform = 'node';
options.alias = {
...(options.alias ?? {}),
'@prisma/client': path.resolve(process.cwd(), 'prisma/generated/client/client.ts'),
};
define: {
__LICENSE_ENDPOINT_ENCODED__: licenseEndpointEncoded,
__LICENSE_ENDPOINT_XOR_KEY__: licenseEndpointXorKey,
},
onSuccess: async () => {
cpSync('src/utils/translations', 'dist/translations', { recursive: true });
Expand Down
Loading