fix(memo): sanitize the tag used in memo export file names
This commit is contained in:
parent
5fef311575
commit
95aa95e986
3 changed files with 112 additions and 3 deletions
68
apps/desktop/tests/red/memo-export-redteam-r1-10.test.ts
Normal file
68
apps/desktop/tests/red/memo-export-redteam-r1-10.test.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { getHistoryService } from '../../src/main/services/HistoryService'
|
||||
import { getMemoService } from '../../src/main/services/MemoService'
|
||||
import {
|
||||
buildMemoExportFileName,
|
||||
sanitizeMemoExportTag,
|
||||
MEMO_EXPORT_TAG_MAX_LENGTH
|
||||
} from '../../src/main/services/memo/memo-export-file-name'
|
||||
import { historyInput, useRedHarness } from './harness'
|
||||
|
||||
useRedHarness()
|
||||
|
||||
describe('메모 내보내기 파일명 정책 (순수)', () => {
|
||||
it('태그가 없으면 memo_<ts>.md', () => {
|
||||
expect(buildMemoExportFileName(undefined, 123)).toBe('memo_123.md')
|
||||
expect(buildMemoExportFileName('', 123)).toBe('memo_123.md')
|
||||
})
|
||||
|
||||
it('경로 구분자·NTFS 예약 문자·제어 문자는 _ 로 바뀐다', () => {
|
||||
expect(sanitizeMemoExportTag('c/c++')).toBe('c_c++')
|
||||
expect(sanitizeMemoExportTag('q:a')).toBe('q_a')
|
||||
expect(sanitizeMemoExportTag('a\\b*c?d"e<f>g|h')).toBe('a_b_c_d_e_f_g_h')
|
||||
expect(sanitizeMemoExportTag('x\u0001y\u007fz')).toBe('x_y_z')
|
||||
})
|
||||
|
||||
it('.. 탐색 조각은 남지 않는다', () => {
|
||||
const name = buildMemoExportFileName('a/../../../x', 1)
|
||||
expect(name).not.toMatch(/[\\/]/)
|
||||
expect(name).not.toContain('..')
|
||||
expect(path.basename(name)).toBe(name)
|
||||
})
|
||||
|
||||
it('한글 태그는 유지되고 공백은 _ 로 바뀐다', () => {
|
||||
expect(sanitizeMemoExportTag('회의 메모')).toBe('회의_메모')
|
||||
})
|
||||
|
||||
it('길이가 제한된다', () => {
|
||||
expect(Array.from(sanitizeMemoExportTag('가'.repeat(500))).length).toBe(MEMO_EXPORT_TAG_MAX_LENGTH)
|
||||
})
|
||||
|
||||
it('안전한 문자가 하나도 없으면 접미사를 생략한다', () => {
|
||||
expect(buildMemoExportFileName('///', 7)).toBe('memo_7.md')
|
||||
expect(buildMemoExportFileName('..', 7)).toBe('memo_7.md')
|
||||
})
|
||||
})
|
||||
|
||||
describe('유스케이스: 특수 문자 태그로 메모 내보내기', () => {
|
||||
function exportWithTag(tag: string): string {
|
||||
const id = getHistoryService().create(historyInput()).id
|
||||
getMemoService().addTag(id, tag)
|
||||
return getMemoService().exportMarkdown({ tag })
|
||||
}
|
||||
|
||||
it.each(['c/c++', 'q:a', 'a/../../../x', 'what?*|<>"'])(
|
||||
'태그 %s 도 exports 폴더 바로 아래에 보이는 파일로 저장된다',
|
||||
(tag) => {
|
||||
const filePath = exportWithTag(tag)
|
||||
expect(path.basename(path.dirname(filePath))).toBe('exports')
|
||||
expect(path.basename(filePath)).not.toMatch(/[\\/:*?"<>|]/)
|
||||
expect(fs.existsSync(filePath)).toBe(true)
|
||||
const listed = fs.readdirSync(path.dirname(filePath))
|
||||
expect(listed).toContain(path.basename(filePath))
|
||||
expect(fs.readFileSync(filePath, 'utf-8')).toContain(`#${tag.toLowerCase()}`)
|
||||
}
|
||||
)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue