Skip to content

Commit

Permalink
GTFS Schedule stop lookup: escape HaltID πŸ›βœ…πŸ”’
Browse files Browse the repository at this point in the history
  • Loading branch information
derhuerst committed Aug 29, 2024
1 parent ac4d7cf commit df30258
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
19 changes: 17 additions & 2 deletions lib/gtfs-stop-by-aus-haltid.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {strictEqual} from 'node:assert/strict'
import QuickLRU from 'quick-lru'
import {connectToPostgres} from './db.js'
import pick from 'lodash/pick.js'
Expand All @@ -10,6 +11,20 @@ const stripDataProviderPrefixFromAusHaltID = (ausHaltId) => {
: ausHaltId
}

// > If pattern does not contain percent signs or underscore, then the pattern only represents the string itself; in that case LIKE acts like the equals operator. An underscore (_) in pattern stands for (matches) any single character; a percent sign (%) matches any string of zero or more characters.
// > To match a literal underscore or percent sign without matching other characters, the respective character in pattern must be preceded by the escape character. […]
// > https://www.postgresql.org/docs/7.3/functions-matching.html
const escapeForLikeOp = (input) => {
return input
.replaceAll('\\', '\\\\')
.replaceAll('%', '\\%')
.replaceAll('_', '\\_')
}
strictEqual(
escapeForLikeOp('foo\\bar\\\\baz%hey_there'),
'foo\\\\bar\\\\\\\\baz\\%hey\\_there',
)

const createQueryGtfsStopByAusHaltID = async (cfg, opt = {}) => {
const {
logger,
Expand Down Expand Up @@ -63,9 +78,9 @@ LIMIT 2
// [0] https://en.wikipedia.org/wiki/Identification_of_Fixed_Objects_in_Public_Transport
// [1] https://www.delfi.de/de/strategie-technik/architektur/
// stations with the DHID format `$country:$region:$station_id`:
`de:%:${strippedHaltId}`,
`de:%:${escapeForLikeOp(strippedHaltId)}`,
// stops/platforms with the DHID format `$country:$region:$station_id:$stop_platform_id`:
`de:%:${strippedHaltId}:%`,
`de:%:${escapeForLikeOp(strippedHaltId)}:%`,
],
})

Expand Down
6 changes: 6 additions & 0 deletions test/gtfs-stop-by-aus-haltid.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ test('works with corresponding Schedule stops only, without parent station', asy
stop_lon: 14.511701,
})
})

test('does not allow `%` SQL injections', async (t) => {
// There is only one stop with a stop_id matching `%:90044994%`, so if we insert `90044994%` unescaped into a `LIKE '%' || $1` query, we obtain a non-ambiguous result through injection.
const stop = await queryGtfsStopByAusHaltID('90044994%')
deepStrictEqual(stop, null, 'must not return a stop')
})

0 comments on commit df30258

Please sign in to comment.