Skip to content

Commit

Permalink
fix(admin-api): fix authenticated SQL injection & remove unnecessary …
Browse files Browse the repository at this point in the history
…WP codepath (#4545)

* fix: fix authenticated SQL injection

* cleanup: remove WP codepath for `all-work` route

* fix: sort in JS to avoid running out of sort memory
  • Loading branch information
marcelgerber authored Feb 17, 2025
1 parent a135b5e commit a7165ab
Showing 1 changed file with 12 additions and 92 deletions.
104 changes: 12 additions & 92 deletions adminSiteServer/apiRoutes/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DbRawPostGdoc, JsonError } from "@ourworldindata/types"

import * as db from "../../db/db.js"
import * as lodash from "lodash"
import path from "path"
import { expectInt } from "../../serverUtils/serverUtil.js"
import { Request } from "../authentication.js"
import e from "express"
Expand All @@ -17,108 +16,29 @@ export async function fetchAllWork(
res: e.Response<any, Record<string, any>>,
trx: db.KnexReadonlyTransaction
) {
type WordpressPageRecord = {
isWordpressPage: number
} & Record<
"slug" | "title" | "subtitle" | "thumbnail" | "authors" | "publishedAt",
string
>
type GdocRecord = Pick<DbRawPostGdoc, "id" | "publishedAt">

const author = req.query.author
const gdocs = await db.knexRaw<GdocRecord>(
trx,
`-- sql
const gdocs = await db
.knexRaw<GdocRecord>(
trx,
`-- sql
SELECT id, publishedAt
FROM posts_gdocs
WHERE JSON_CONTAINS(content->'$.authors', '"${author}"')
WHERE JSON_CONTAINS(content->'$.authors', ?)
AND type NOT IN ("data-insight", "fragment")
AND published = 1
`
)

// type: page
const wpModularTopicPages = await db.knexRaw<WordpressPageRecord>(
trx,
`-- sql
SELECT
wpApiSnapshot->>"$.slug" as slug,
wpApiSnapshot->>"$.title.rendered" as title,
wpApiSnapshot->>"$.excerpt.rendered" as subtitle,
TRUE as isWordpressPage,
wpApiSnapshot->>"$.authors_name" as authors,
wpApiSnapshot->>"$.featured_media_paths.medium_large" as thumbnail,
wpApiSnapshot->>"$.date" as publishedAt
FROM posts p
WHERE wpApiSnapshot->>"$.content" LIKE '%topic-page%'
AND JSON_CONTAINS(wpApiSnapshot->'$.authors_name', '"${author}"')
AND wpApiSnapshot->>"$.status" = 'publish'
AND NOT EXISTS (
SELECT 1 FROM posts_gdocs pg
WHERE pg.slug = p.slug
AND pg.content->>'$.type' LIKE '%topic-page'
)
`
)

const isWordpressPage = (
post: WordpressPageRecord | GdocRecord
): post is WordpressPageRecord =>
(post as WordpressPageRecord).isWordpressPage === 1

function* generateProperty(key: string, value: string) {
yield `${key}: ${value}\n`
}

const sortByDateDesc = (
a: GdocRecord | WordpressPageRecord,
b: GdocRecord | WordpressPageRecord
): number => {
if (!a.publishedAt || !b.publishedAt) return 0
return (
new Date(b.publishedAt).getTime() -
new Date(a.publishedAt).getTime()
`,
[`"${author}"`]
)
}
.then((rows) => lodash.orderBy(rows, (row) => row.publishedAt, "desc"))

function* generateAllWorkArchieMl() {
for (const post of [...gdocs, ...wpModularTopicPages].sort(
sortByDateDesc
)) {
if (isWordpressPage(post)) {
yield* generateProperty(
"url",
`https://ourworldindata.org/${post.slug}`
)
yield* generateProperty("title", post.title)
yield* generateProperty("subtitle", post.subtitle)
yield* generateProperty(
"authors",
JSON.parse(post.authors).join(", ")
)
const parsedPath = path.parse(post.thumbnail)
yield* generateProperty(
"filename",
// /app/uploads/2021/09/reducing-fertilizer-768x301.png -> reducing-fertilizer.png
path.format({
name: parsedPath.name.replace(/-\d+x\d+$/, ""),
ext: parsedPath.ext,
})
)
yield "\n"
} else {
// this is a gdoc
yield* generateProperty(
"url",
`https://docs.google.com/document/d/${post.id}/edit`
)
yield "\n"
}
}
}
const archieLines = gdocs.map(
(post) => `url: https://docs.google.com/document/d/${post.id}/edit`
)

res.type("text/plain")
return [...generateAllWorkArchieMl()].join("")
return archieLines.join("\n\n")
}

export async function fetchNamespaces(
Expand Down

0 comments on commit a7165ab

Please sign in to comment.