Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 472fa25bae | |||
| aab4f12fc4 |
+135
-135
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Kanban</title>
|
<title>Kanban</title>
|
||||||
<script type="module" crossorigin src="/assets/index-ZZWeneKP.js"></script>
|
<script type="module" crossorigin src="/assets/index-DT3pghXY.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-b0xjFtx2.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-b0xjFtx2.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ func handleUploadCardFile(db *DB, workdir string) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fname := safeFilename(header.Filename)
|
fname := safeFilename(header.Filename)
|
||||||
fileID := newID()
|
|
||||||
storedPath := filepath.Join(dir, randomFilePrefix()+"__"+fname)
|
storedPath := filepath.Join(dir, randomFilePrefix()+"__"+fname)
|
||||||
|
|
||||||
out, err := os.Create(storedPath)
|
out, err := os.Create(storedPath)
|
||||||
@@ -237,16 +236,12 @@ func handleUploadCardFile(db *DB, workdir string) http.HandlerFunc {
|
|||||||
|
|
||||||
actor, _ := infra.UserIDFromContext(r.Context(), userCtxKey)
|
actor, _ := infra.UserIDFromContext(r.Context(), userCtxKey)
|
||||||
|
|
||||||
// Use the random-prefixed path on disk but a stable file id in the DB.
|
|
||||||
cf, err := db.CreateCardFile(cardID, actor, fname, mimeType, storedPath, source, written)
|
cf, err := db.CreateCardFile(cardID, actor, fname, mimeType, storedPath, source, written)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(storedPath)
|
os.Remove(storedPath)
|
||||||
serverError(w, err)
|
serverError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// We generated newID() in CreateCardFile; align the on-disk filename with that id
|
|
||||||
// is not required since stored_path is what we serve from.
|
|
||||||
_ = fileID
|
|
||||||
|
|
||||||
infra.HTTPJSONResponse(w, http.StatusCreated, cf)
|
infra.HTTPJSONResponse(w, http.StatusCreated, cf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,24 @@ type Token = ImgToken | LinkToken | TextToken;
|
|||||||
|
|
||||||
const TOKEN_RE = /(!\[([^\]\n]*)\]\(([^)\s]+)\))|(\[([^\]\n]+)\]\(([^)\s]+)\))/g;
|
const TOKEN_RE = /(!\[([^\]\n]*)\]\(([^)\s]+)\))|(\[([^\]\n]+)\]\(([^)\s]+)\))/g;
|
||||||
|
|
||||||
|
// Allow only safe URL schemes. Reject javascript:, data:text/html, vbscript:, etc.
|
||||||
|
// Accepts: absolute http(s), protocol-relative //, and same-origin paths (/...).
|
||||||
|
function safeURL(url: string): string | null {
|
||||||
|
const u = url.trim();
|
||||||
|
if (u.startsWith("/")) return u;
|
||||||
|
if (/^https?:\/\//i.test(u)) return u;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data: scheme is allowed only when the MIME prefix is image/.
|
||||||
|
function safeImageURL(url: string): string | null {
|
||||||
|
const safe = safeURL(url);
|
||||||
|
if (safe) return safe;
|
||||||
|
const u = url.trim();
|
||||||
|
if (/^data:image\/[a-z0-9.+-]+(;[a-z0-9-]+=[^,]+)*;base64,/i.test(u)) return u;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function tokenize(input: string): Token[] {
|
function tokenize(input: string): Token[] {
|
||||||
const out: Token[] = [];
|
const out: Token[] = [];
|
||||||
let last = 0;
|
let last = 0;
|
||||||
@@ -24,9 +42,19 @@ function tokenize(input: string): Token[] {
|
|||||||
out.push({ kind: "text", value: input.slice(last, m.index) });
|
out.push({ kind: "text", value: input.slice(last, m.index) });
|
||||||
}
|
}
|
||||||
if (m[1]) {
|
if (m[1]) {
|
||||||
out.push({ kind: "img", alt: m[2] || "", url: m[3] });
|
const url = safeImageURL(m[3]);
|
||||||
|
if (url) {
|
||||||
|
out.push({ kind: "img", alt: m[2] || "", url });
|
||||||
|
} else {
|
||||||
|
out.push({ kind: "text", value: m[0] });
|
||||||
|
}
|
||||||
} else if (m[4]) {
|
} else if (m[4]) {
|
||||||
out.push({ kind: "link", label: m[5], url: m[6] });
|
const url = safeURL(m[6]);
|
||||||
|
if (url) {
|
||||||
|
out.push({ kind: "link", label: m[5], url });
|
||||||
|
} else {
|
||||||
|
out.push({ kind: "text", value: m[0] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last = TOKEN_RE.lastIndex;
|
last = TOKEN_RE.lastIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user