Skip to content
Merged
Changes from all commits
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
68 changes: 61 additions & 7 deletions hooks/use-bounty-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import { fetcher } from "@/lib/graphql/client";
import {
ReviewSubmissionDocument,
type BountyQuery,
type DisputeReasonEnum,
type ReviewSubmissionMutation,
type ReviewSubmissionMutationVariables,
} from "@/lib/graphql/generated";
import type { ContributorProgress, Bounty, Milestone } from "@/types/bounty";
import { escrowKeys } from "./use-escrow";
import { EscrowService } from "@/lib/services/escrow";
import type { EscrowPool } from "@/types/escrow";
import { post } from "@/lib/api/client";

export type ExtendedBountyQuery = Omit<BountyQuery, "bounty"> & {
bounty?: BountyQuery["bounty"] & Partial<Bounty>;
};

export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export const delay = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));

// ---------------------------------------------------------------------------
// Contract client shape (resolved from globalThis.__applicationContracts)
Expand Down Expand Up @@ -400,7 +403,10 @@ export function useApplyForSlot() {
});
}
// Static memory storage for messages
const recordedMessages: Record<string, Array<{ contributorId: string; message: string; timestamp: string }>> = {};
const recordedMessages: Record<
string,
Array<{ contributorId: string; message: string; timestamp: string }>
> = {};

export function useReleasePayment(bountyId: string) {
const queryClient = useQueryClient();
Expand Down Expand Up @@ -552,9 +558,12 @@ export function useRemoveContributor(bountyId: string) {
if (previous?.bounty) {
const contributorProgress: ContributorProgress[] =
previous.bounty.contributorProgress || [];

// Decrement total slots occupied by 1
const occupied = Math.max(0, (previous.bounty.totalSlotsOccupied ?? 1) - 1);
const occupied = Math.max(
0,
(previous.bounty.totalSlotsOccupied ?? 1) - 1,
);

queryClient.setQueryData<ExtendedBountyQuery>(
bountyKeys.detail(bountyId),
Expand Down Expand Up @@ -593,7 +602,7 @@ export function useSendMessage(bountyId: string) {
message: string;
}) => {
await delay(1000);

// Store in static memory for real message logging/recording
if (!recordedMessages[bountyId]) {
recordedMessages[bountyId] = [];
Expand All @@ -603,10 +612,55 @@ export function useSendMessage(bountyId: string) {
message,
timestamp: new Date().toISOString(),
});

console.log(`[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}, message="${message}"`);

console.log(
`[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}, message="${message}"`,
);
Comment on lines +615 to +618

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid logging raw user message content.

This logs the full message body and contributorId on every send. Message text is user-generated data, and emitting it verbatim risks leaking sensitive content into production logs (and adds noise). Consider gating behind a debug flag or dropping the message payload from the log.

🔒 Proposed adjustment
-      console.log(
-        `[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}, message="${message}"`,
-      );
+      if (process.env.NODE_ENV !== "production") {
+        console.log(
+          `[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}`,
+        );
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(
`[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}, message="${message}"`,
);
if (process.env.NODE_ENV !== "production") {
console.log(
`[useSendMessage] Recorded message for bountyId ${bountyId}: contributorId=${contributorId}`,
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hooks/use-bounty-application.ts` around lines 615 - 618, The console.log in
useSendMessage currently prints raw user content (message) and contributorId;
remove or redact the message payload and avoid emitting contributorId in
production logs—either gate the detailed log behind a debug flag (e.g.,
process.env.DEBUG or a provided isDebug variable) or log only non-sensitive
metadata such as bountyId and message length or a redaction token; update the
console.log call in useSendMessage to output safe info (e.g.,
`bountyId=${bountyId}, messageLength=${message?.length}`) or wrap the detailed
message output in a debug-only branch.

return { contributorId, message };
},
});
}

// ---------------------------------------------------------------------------
// Hook: raise dispute
// ---------------------------------------------------------------------------

export interface RaiseDisputeInput {
bountyId: string;
reason: DisputeReasonEnum;
description: string;
}

export interface RaiseDisputeResult {
id: string;
campaignId: string;
reason: string;
description: string;
status: string;
createdAt: string;
}

/**
* Submits a new dispute for a bounty via the REST API.
*
* On success it returns the created dispute (including its `id`) and
* invalidates the bounty detail query so the UI reflects the new DISPUTED
* status immediately.
*/
export function useRaiseDispute() {
const qc = useQueryClient();

return useMutation<RaiseDisputeResult, Error, RaiseDisputeInput>({
mutationFn: async ({ bountyId, reason, description }) => {
return post<RaiseDisputeResult>("/api/disputes", {
campaignId: bountyId,
reason,
description,
});
},
onSuccess: (_data, variables) => {
qc.invalidateQueries({ queryKey: bountyKeys.detail(variables.bountyId) });
qc.invalidateQueries({ queryKey: bountyKeys.lists() });
},
});
}
Loading