phase one of pingback receive XML-RPC implementation
This commit is contained in:
parent
f3e2d1d1c9
commit
a7a4714ea0
|
@ -0,0 +1,43 @@
|
|||
|
||||
const config = require('./../config')
|
||||
const parser = require("fast-xml-parser")
|
||||
|
||||
/**
|
||||
Sample XML:
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/kristien.html</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://kristienthoelen.be/2021/03/22/de-stadia-van-een-burn-out-in-welk-stadium-zit-jij/</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
*/
|
||||
const isValidDomain = (url) => {
|
||||
return config.allowedWebmentionSources.some(domain => {
|
||||
return url.indexOf(domain) !== -1
|
||||
})
|
||||
}
|
||||
|
||||
function validate(body) {
|
||||
const xml = parser.parse(body)
|
||||
if(!xml) return false
|
||||
if(!xml.methodCall || xml.methodCall.methodName !== "pingback.ping") return false
|
||||
if(!xml.methodCall.params || !xml.methodCall.params.param || xml.methodCall.params.param.length !== 2) return false
|
||||
if(!xml.methodCall.params.param.every(param => param?.value?.string?.startsWith('http'))) return false
|
||||
if(!isValidDomain(xml.methodCall.params.param[1].value.string)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
async function receive(body) {
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
receive,
|
||||
validate
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
const pingbackReceiver = require('./receive')
|
||||
|
||||
function success(msg) {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodResponse>
|
||||
<params>
|
||||
<param>
|
||||
<value>
|
||||
<string>
|
||||
${msg}
|
||||
</string>
|
||||
</value>
|
||||
</param>
|
||||
</params>
|
||||
</methodResponse>
|
||||
`
|
||||
}
|
||||
|
||||
function err(e) {
|
||||
console.err(` -- pingback receive went wrong: ${e.message}`)
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodResponse>
|
||||
<fault>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>
|
||||
faultCode
|
||||
</name>
|
||||
<value>
|
||||
<int>
|
||||
0
|
||||
</int>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>
|
||||
faultString
|
||||
</name>
|
||||
<value>
|
||||
<string>
|
||||
${e.message}
|
||||
</string>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</fault>
|
||||
</methodResponse>`
|
||||
}
|
||||
|
||||
function route(router) {
|
||||
router.post("pingback receive endpoint", "/pingback", async (ctx) => {
|
||||
try {
|
||||
if(!pingbackReceiver.validate(ctx.request.body)) {
|
||||
throw "malformed pingback request"
|
||||
}
|
||||
|
||||
// we do NOT await this on purpose.
|
||||
pingbackReceiver.receive(ctx.request.body)
|
||||
|
||||
ctx.status = 200
|
||||
ctx.body = success("Thanks, bro. Will process this pingback soon, pinky swear!")
|
||||
} catch(e) {
|
||||
ctx.status = 200
|
||||
ctx.body = err(e)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
route
|
||||
}
|
|
@ -29,6 +29,7 @@ app.use(bodyParser({
|
|||
|
||||
// route docs: https://github.com/koajs/router/blob/HEAD/API.md#module_koa-router--Router+get%7Cput%7Cpost%7Cpatch%7Cdelete%7Cdel
|
||||
require("./webmention/route").route(router);
|
||||
require("./pingback/route").route(router);
|
||||
const config = require("./config");
|
||||
config.setupDataDirs();
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
describe("pingback receive validation tests", () => {
|
||||
|
||||
const { validate } = require('../../src/pingback/receive')
|
||||
|
||||
test("not valid if malformed XML as body", () => {
|
||||
const result = validate("ola pola")
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if methodName is not pingback.ping", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>ka.tsjing</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if less than two parameters", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if more than two parameters", () => {
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`
|
||||
|
||||
expect(validate(xml)).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if target is not in trusted domains from config", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://flashballz.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if target is not http(s)", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>gemini://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("not valid if source is not http(s)", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>gemini://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("is valid if pingback.ping and two http(s) parameters of which target is trusted", () => {
|
||||
const result = validate(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>pingback.ping</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>https://cool.site</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>https://brainbaking.com/post/2021/03/cool-ness</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
`)
|
||||
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
|
||||
})
|
Loading…
Reference in New Issue