Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
145662b
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
60ecb66
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
5c50f79
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
935fc69
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
7711b47
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
f2b3d4c
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
b27e02a
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 12, 2026
3d4551f
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 13, 2026
0bd6a51
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 13, 2026
4566ff9
feat(web): update terminology and improve home page copy
google-labs-jules[bot] May 13, 2026
9fc3b2e
fix(e2e): update terms in e2e locators for upload heading and org pap…
google-labs-jules[bot] May 13, 2026
1d0ed4c
fix: address review comments and improve test coverage for patch thre…
Copilot May 13, 2026
0b6dfb8
chore: restore package-lock.json to pre-commit state
Copilot May 13, 2026
c8f6e21
fix: address code review and update test terminology
google-labs-jules[bot] May 13, 2026
8a9d8bf
refactor: map tab buttons from array
is0692vs May 14, 2026
1da015d
test: change getByText to findByText in user page test to await async…
is0692vs May 14, 2026
8aef7c5
chore: merge staging into feature branch
is0692vs May 14, 2026
74fe117
fix: respond to code review on tab formatting and test titles
google-labs-jules[bot] May 14, 2026
b0f8f21
Merge branch 'staging' into jules-7342432796452625521-cd747fc1
is0692vs May 14, 2026
969b2e6
fix(e2e): update terminology locators and address PR review comments
google-labs-jules[bot] May 14, 2026
7a4f604
feat(ui): add drag-and-drop feedback to file upload area
google-labs-jules[bot] May 14, 2026
be4d9b0
feat: 非同期処理ボタンにローディングスピナーを追加
google-labs-jules[bot] May 14, 2026
48b02f4
Update apps/web/src/app/upload/page.tsx
is0692vs May 14, 2026
52ac778
test(ui): add unit tests for drag-and-drop file upload
google-labs-jules[bot] May 14, 2026
e99f462
🛡️ Sentinel: [HIGH] SQLワイルドカードインジェクションの脆弱性を修正
google-labs-jules[bot] May 15, 2026
3ada131
fix: レビューフィードバックの反映とUI改善
google-labs-jules[bot] May 15, 2026
0fb3e9a
fix(ui): address drag-and-drop feedback bugs and test coverage
google-labs-jules[bot] May 15, 2026
de0721c
Merge branch 'staging' into jules-7342432796452625521-cd747fc1
is0692vs May 15, 2026
5b54a2c
タグ入力で全角カンマや読点(,、)でも分割できるように修正
google-labs-jules[bot] May 15, 2026
544bc79
タグ入力時に末尾のカンマがなくても入力中の最後のタグを反映するように修正
google-labs-jules[bot] May 15, 2026
a679b1d
タグ入力時に末尾のカンマがなくても入力中の最後のタグを反映するように修正
google-labs-jules[bot] May 15, 2026
66979f8
fix(tags): show chips for all tags including the last one without tra…
google-labs-jules[bot] May 15, 2026
46296ab
🛡️ Sentinel: [HIGH] SQLワイルドカードインジェクションの脆弱性を修正
google-labs-jules[bot] May 15, 2026
cdc2dac
Merge pull request #806 from Hiroki-org/main
is0692vs May 15, 2026
8aa76f0
Merge remote-tracking branch 'origin/staging' into jules-108569701012…
is0692vs May 15, 2026
2fbebd4
fix: support full-width tag delimiters
is0692vs May 15, 2026
82bd50a
Merge remote-tracking branch 'origin/staging' into jules-554732398972…
is0692vs May 15, 2026
bb0889f
test: harden SQL wildcard escaping coverage
is0692vs May 15, 2026
0687f88
fix(tags): show chips for all tags including the last one without tra…
google-labs-jules[bot] May 15, 2026
a45c77b
fix: bound tag suggestion query length
is0692vs May 15, 2026
8e97ef2
fix: restore full-width tag delimiter support
is0692vs May 15, 2026
c8f6df1
fix: preserve async button loading labels
is0692vs May 15, 2026
441ace6
Merge pull request #807 from Hiroki-org/jules-5547323989720427958-e09…
is0692vs May 15, 2026
2b54e7c
Merge remote-tracking branch 'origin/staging' into palette/async-butt…
is0692vs May 15, 2026
53794cc
fix(tags): show chips for all tags including the last one without tra…
google-labs-jules[bot] May 15, 2026
a011086
fix: restore full-width tag delimiter support
is0692vs May 15, 2026
9e7d131
Merge remote-tracking branch 'origin/staging' into jules-108569701012…
is0692vs May 15, 2026
3565ef7
Merge remote-tracking branch 'origin/staging' into palette-upload-dnd…
is0692vs May 15, 2026
bc66c05
fix(tags): show chips for all tags including the last one without tra…
google-labs-jules[bot] May 15, 2026
0e9c124
fix: harden upload drag-and-drop validation
is0692vs May 15, 2026
523d670
fix: prevent concurrent invite requests
is0692vs May 15, 2026
1d65784
fix: address terminology PR review issues
is0692vs May 15, 2026
01a89ac
Merge remote-tracking branch 'origin/staging' into jules-734243279645…
is0692vs May 15, 2026
0756bdd
Merge pull request #805 from Hiroki-org/palette/async-button-loading-…
is0692vs May 15, 2026
b5b7273
Revert "fix(tags): show chips for all tags including the last one wit…
is0692vs May 15, 2026
614e587
Merge remote-tracking branch 'origin/staging' into jules-108569701012…
is0692vs May 15, 2026
efe32d8
test: cover tag input splitting
is0692vs May 15, 2026
eafac23
Merge remote-tracking branch 'origin/staging' into palette-upload-dnd…
is0692vs May 15, 2026
3cc5d85
fix: validate upload MIME types
is0692vs May 15, 2026
7e83b0a
Merge remote-tracking branch 'origin/staging' into jules-734243279645…
is0692vs May 15, 2026
df030b0
Merge pull request #808 from Hiroki-org/jules-10856970101262783403-fa…
is0692vs May 15, 2026
e897155
Merge remote-tracking branch 'origin/staging' into palette-upload-dnd…
is0692vs May 15, 2026
940f796
fix: ignore non-file upload drags
is0692vs May 15, 2026
ec2059b
Merge pull request #804 from Hiroki-org/palette-upload-dnd-ux-1348014…
is0692vs May 15, 2026
bb3fd0a
Merge remote-tracking branch 'origin/staging' into jules-734243279645…
is0692vs May 15, 2026
eb00f31
test: cover terminology fallback states
is0692vs May 15, 2026
bafbd7b
fix: address terminology review feedback
is0692vs May 15, 2026
1b35df4
fix: keep creation loading indicators
is0692vs May 15, 2026
64a695c
Merge pull request #784 from Hiroki-org/jules-7342432796452625521-cd7…
is0692vs May 15, 2026
5ede472
🛡️ Sentinel: [CRITICAL] 修正: IPスプーフィングの脆弱性
google-labs-jules[bot] May 15, 2026
3a3ecdf
Merge pull request #810 from Hiroki-org/sentinel/fix-ip-spoofing-vuln…
is0692vs May 16, 2026
da0a9fb
🧹 [code health] Remove unused console.log in test file
google-labs-jules[bot] May 17, 2026
53c66d6
Merge remote-tracking branch 'origin/staging' into jules-code-health-…
is0692vs May 18, 2026
291a69f
Merge pull request #820 from Hiroki-org/jules-code-health-markdown-te…
is0692vs May 18, 2026
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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@
**Issue:** String exceptions were not handled, causing the app to return an Internal Server Error.
**Learning:** `e instanceof Error` correctly identifies built-in errors, but doesn't handle `throw "error"`. It caused Hono to not correctly propagate a 500 status.
**Prevention:** Handled by validating `e instanceof Error ? e.message : typeof e === 'string' ? e : "";` and throwing an error properly at the end `throw e instanceof Error ? e : new Error(String(e));`.
## 2026-05-15 - [IPスプーフィングの脆弱性]
**Vulnerability:** X-Forwarded-For ヘッダーを使用した IP アドレスのフォールバック
**Learning:** Cloudflare Workers 環境などでは CF-Connecting-IP が信頼できる IP アドレスのソースとなります。X-Forwarded-For にフォールバックすると、クライアントがヘッダーを偽装(スプーフィング)し、IP ベースのアクセス制御やレート制限を回避できる可能性があります。
**Prevention:** 信頼できるロードバランサーや CDN が設定するヘッダー(例:CF-Connecting-IP)のみを使用し、クライアントから送信される可能性のあるヘッダー(例:X-Forwarded-For)へのフォールバックは避ける。
21 changes: 21 additions & 0 deletions apps/api/src/routes/__tests__/orgs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2213,4 +2213,25 @@ describe("orgs routes", () => {
expect(body.error).toContain("last admin");
});
});

describe("GET /api/orgs/:slug/papers algorithmic DoS prevention", () => {
it("escapes wildcard characters correctly in papers query endpoint", async () => {
const app = await createTestApp();
const env = createTestEnv();

queueSelectResponses([
{ getResult: { id: "org-1", slug: "test" } },
{ getResult: { id: "user-1", role: "admin" } },
{ allResult: [] }
]);

const res = await app.request(
"http://localhost/api/orgs/test/papers?venue=%25%5C_", // %\_
{},
env as any
);

expect(res.status).toBe(200);
});
});
});
170 changes: 100 additions & 70 deletions apps/api/src/routes/__tests__/papers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2161,11 +2161,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2209,11 +2211,13 @@ describe("papers routes", () => {
githubId: "123",
name: "Uploader",
});
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);

const app = await createTestApp();
const env = createTestEnv();
Expand Down Expand Up @@ -2367,11 +2371,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2417,11 +2423,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2457,11 +2465,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2495,11 +2505,13 @@ describe("papers routes", () => {
githubId: "123",
name: "Uploader",
});
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);

const app = await createTestApp();
const env = createTestEnv();
Expand Down Expand Up @@ -2529,11 +2541,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2569,11 +2583,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2611,11 +2627,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2651,11 +2669,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2691,11 +2711,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2739,11 +2761,13 @@ describe("papers routes", () => {
githubId: "123",
name: "Uploader",
});
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);

const app = await createTestApp();
const env = createTestEnv();
Expand Down Expand Up @@ -2897,11 +2921,13 @@ describe("papers routes", () => {
});
const set = vi.fn().mockReturnThis();
const where = vi.fn().mockReturnThis();
mockDb.select = vi.fn().mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.select = vi
.fn()
.mockImplementation(() =>
makeQuery({
getResult: { paperId: "paper-1", userId: "user-1", role: "uploader" },
}),
);
mockDb.update = vi.fn().mockImplementation(() => ({ set, where }) as any);

const app = await createTestApp();
Expand Down Expand Up @@ -2940,11 +2966,13 @@ describe("papers routes", () => {
});

it("GET /api/papers/:id returns 401 for private paper without Bearer token", async () => {
mockDb.select = vi.fn().mockImplementationOnce(() =>
makeQuery({
getResult: { id: "paper-1", title: "P1", visibility: "private" },
}),
);
mockDb.select = vi
.fn()
.mockImplementationOnce(() =>
makeQuery({
getResult: { id: "paper-1", title: "P1", visibility: "private" },
}),
);

const app = await createTestApp();
const env = createTestEnv();
Expand Down Expand Up @@ -3201,6 +3229,7 @@ describe("papers routes", () => {

expect(res.status).toBe(500);
});

it("POST /api/papers/:id/invites handles non-string non-Error rejections", async () => {
const token = await createTestJWT({
sub: "user-uploader",
Expand Down Expand Up @@ -3238,6 +3267,7 @@ describe("papers routes", () => {

expect(res.status).toBe(500);
});

});
});

Expand Down
Loading
Loading