Czy mogę użyć require („path”). Join w celu bezpiecznego łączenia adresów URL?

128

Czy jest to bezpieczne do require("path").joinłączenia adresów URL, na przykład:

require("path").join("http://example.com", "ok"); 
//returns 'http://example.com/ok'

require("path").join("http://example.com/", "ok"); 
//returns 'http://example.com/ok'

Jeśli nie, w jaki sposób możesz to zrobić bez pisania pełnego kodu ifs?

Renato Gama
źródło
3
Zobacz też github.com/joyent/node/issues/2216
Colonel Panic
5
W przypadku, gdy ktoś chce korzystać path.join, ale uniknąć problemów w systemie Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn
5
@TimothyZorn Problem polega na tym, że jeśli zrobisz coś takiego path.posix.join('http://localhost:9887/one/two/three/', '/four'), złączenie pozbywa się jednego z podwójnych cięć whttp://
Max Alexander
Ach, tak - słuszna uwaga. W tych scenariuszach chciałbyś zrobić coś podobnego 'http://localhost:9887/one/two/three/'.replace(/^\/+|\/+$/, '') + '/' + '/four'.replace(/^\/+|\/+$/, '')i możesz to zrobić, String.prototype.trimSlashes = function() { return this.replace(/^\/+|\/+$/, ''); }jeśli nie chcesz w kółko wpisywać wyrażenia regularnego. stackoverflow.com/a/22387870/2537258
Timothy Zorn
lub['http://localhost:9887/one/two/three/', '/four'].map((part) => part. replace(/^\/+|\/+$/, '')).join('/')
Timothy Zorn

Odpowiedzi:

142

Nie path.join()zwróci nieprawidłowe wartości w przypadku użycia z adresami URL.

Brzmi tak, jak chcesz url.resolve. Z dokumentacji Node :

url.resolve('/one/two/three', 'four')         // '/one/two/four'
url.resolve('http://example.com/', '/one')    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

Edycja: Jak słusznie wskazuje Andreas w komentarzu, url.resolvepomogłoby to tylko wtedy, gdy problem jest tak prosty, jak na przykładzie. url.parsedotyczy również tego pytania, ponieważ zwraca ono spójnie i przewidywalnie sformatowane pola za pośrednictwem URLobiektu, co zmniejsza potrzebę stosowania „kodu pełnego if”.

Matthew Bakaitis
źródło
1
Chociaż nie do końca to, czego szukałem, rozwiązuje również mój problem. Dzięki za pomoc!
Renato Gama
6
@AndreasHultgren pierwszy komentarz jest poprawny. Gdyby przykład był, url.resolve('/one/two/three/', 'four')wynik byłby 'one/two/three/four'.
tavnab
3
W przypadku, gdy ktoś chce korzystać path.join, ale uniknąć problemów w systemie Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn
2
Komentarze są błędne, url.resolve('/one/two/three', 'four') // '/one/two/four'w odpowiedzi jest poprawna
Jonathan.
2
Należy również pamiętać, url.resolve()że przyjmuje tylko 2 argumenty, gdzie as path.join()przyjmuje dowolną liczbę. W zależności od tego, co robisz, może być konieczne zagnieżdżanie połączeń, np. ...url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
Molomby,
47

Nie, nie powinieneś używać path.join()do łączenia elementów URL.

Jest pakiet do tego teraz. Więc zamiast wymyślać koło na nowo, pisz własne testy, znajduj błędy, naprawiaj je, pisz więcej testów, znajdź skrajny przypadek, w którym to nie działa itp., Możesz użyć tego pakietu.

dołączenie url

https://github.com/jfromaniello/url-join

zainstalować

npm install url-join

Stosowanie

var urljoin = require('url-join');

var fullUrl = urljoin('http://www.google.com', 'a', '/b/cd', '?foo=123');

console.log(fullUrl);

Wydruki:

http://www.google.com/a/b/cd?foo=123

złóg
źródło
1
To. To jest fantastyczne. Dziękuję Ci.
dudewad
6

Kiedy próbowałem PATH do łączenia części adresów URL, napotykam problemy. PATH.joinstripes '//' aż do '/' iw ten sposób unieważnia bezwzględny adres URL (np. http: // ... -> http: / ...). Dla mnie szybkim rozwiązaniem było:

baseurl.replace(/\/$/,"") + '/' + path.replace(/^\//,"") )

lub z rozwiązaniem opublikowanym przez Colonel Panic:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")
Piotr
źródło
Co jeśli próbuję zbudować URL korzeniowy względne tak: /assets/foo? Spowoduje to powstanie adresu URL względem bieżącej ścieżki assets/foo.
Andrey Mikhaylov - lolmaus
5

Nie! W systemie Windows path.joindołączy z ukośnikiem odwrotnym. Adresy URL HTTP są zawsze ukośnikami.

Co powiesz na

> ["posts", "2013"].join("/")
'posts/2013'
Colonel Panic
źródło
Dobry pomysł, ale co, jeśli pierwszy argument ma już na końcu ukośnik? np .: ["posts/", "2013"].join("/")?
Renato Gama
1
@RenatoGama, posts//2013nadal jest prawidłowym adresem URL.
Goodwine
2
^, który nie będzie działał we wszystkich domenach, mimo że jest prawidłowym identyfikatorem URI.
BingeBoy
2
W szczególności Node's Express nie ignoruje dodatkowych ukośników podczas routingu.
Perseids
1
W przypadku, gdy ktoś chce korzystać path.join, ale uniknąć problemów w systemie Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn
5

Robimy to tak:

var _ = require('lodash');

function urlJoin(a, b) {
  return _.trimEnd(a, '/') + '/' + _.trimStart(b, '/');
}
Peter Dotchev
źródło
3

Jeśli używasz lodash , możesz użyć tego prostego onelinera:

// returns part1/part2/part3
['part1/', '/part2', '/part3/'].map((s) => _.trim(s, '/')).join('/')

zainspirowany @Peter Dotchev za odpowiedź

MK
źródło
3

Jeśli używasz Angular, możesz użyć lokalizacji :

import { Location } from '@angular/common';
// ...
Location.joinWithSlash('beginning', 'end');

Działa jednak tylko na 2 argumentach, więc w razie potrzeby musisz łączyć wywołania lub napisać funkcję pomocniczą, aby to zrobić.

Qortex
źródło
2

Oto czego używam:

function joinUrlElements() {
  var re1 = new RegExp('^\\/|\\/$','g'),
      elts = Array.prototype.slice.call(arguments);
  return elts.map(function(element){return element.replace(re1,""); }).join('/');
}

przykład:

url = joinUrlElements(config.mgmtServer, '/v1/o/', config.org, '/apps');
Ser
źródło
Co jeśli próbuję zbudować URL korzeniowy względne tak: /assets/foo? Spowoduje to powstanie adresu URL względem bieżącej ścieżki assets/foo.
Andrey Mikhaylov - lolmaus
1
dodać ukośnik? To znaczy, to prosta kontrola; możesz dodać to samodzielnie.
Cheeso
4
Tak to się zaczyna ... Następna rzecz, którą wiesz, że spędziłeś łącznie ponad 8 godzin na znajdowaniu przypadków skrajnych, które nie działają, i naprawianiu ich w trakcie projektu.
kamień
2

Obiekt WHATWG URL konstruktor ma (input, base)wersję, a inputmoże być względna użyciu /, ./, ../. Połącz to z path.posix.joini możesz zrobić wszystko:

const {posix} = require ("path");
const withSlash = new URL("https://example.com:8443/something/");
new URL(posix.join("a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("./a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("/a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
new URL(posix.join("../a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
const noSlash = new URL("https://example.com:8443/something");
new URL(posix.join("./a", "b", "c"), noSlash).toString(); // 'https://example.com:8443/a/b/c'
Coderer
źródło
0

Niestandardowe rozwiązanie dla maszynopisu:

export function pathJoin(parts: string[], sep: string) {
  return parts
    .map(part => {
      const part2 = part.endsWith(sep) ? part.substring(0, part.length - 1) : part;
      return part2.startsWith(sep) ? part2.substr(1) : part2;
    })
    .join(sep);
}

expect(pathJoin(['a', 'b', 'c', 'd'], '/')).toEqual('a/b/c/d');
expect(pathJoin(['a/', '/b/', 'c/', 'd'], '/')).toEqual('a/b/c/d');
expect(pathJoin(['http://abc.de', 'users/login'], '/')).toEqual('http://abc.de/users/login');
Patrick Wozniak
źródło
0

Moje rozwiązanie

path.join(SERVER_URL, imageAbsolutePath).replace(':/','://');

Edycja: jeśli chcesz obsługiwać środowiska Windows

path.join(SERVER_URL, imageAbsolutePath).replace(/\\/g,'/').replace(':/','://');

Drugie rozwiązanie zastąpi wszystkie odwrotne ukośniki, więc części adresu URL, takie jak querystring i hash, mogą również zostać zmienione, ale temat łączy się tylko ze ścieżką adresu URL, więc nie uważam tego za problem.

Killy
źródło
0

Istnieją inne działające odpowiedzi, ale poszedłem z następującymi. Mała kombinacja path.join / URL.

const path = require('path');
//
const baseUrl = 'http://ejemplo.mx';
// making odd shaped path pieces to see how they're handled.
const pieces = ['way//', '//over/', 'there/'];
//
console.log(new URL(path.join(...pieces), baseUrl).href);
// http://ejemplo.mx/way/over/there/

// path.join expects strings. Just an example how to ensure your pieces are Strings.
const allString = ['down', 'yonder', 20000].map(String);
console.log(new URL(path.join(...allString), baseUrl).href);
// http://ejemplo.mx/down/yonder/20000
Neil Guy Lindberg
źródło
0

Można to osiągnąć za pomocą kombinacji ścieżki i adresu URL węzła :

  1. Wymagaj pakietów:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Zacznij od utworzenia obiektu URL do pracy z:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Użyj pathname=i path.joindo skonstruowania dowolnej możliwej kombinacji:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(możesz zobaczyć, jak liberalny path.joinjest z argumentami)

  1. W tym momencie Twój adres URL odzwierciedla ostateczny pożądany wynik:
> myUrl.toString()
'https://example.com/search/for/something/'

Dlaczego takie podejście?

Ta technika wykorzystuje wbudowane biblioteki. Im mniej zależności zewnętrznych, tym lepiej, jeśli chodzi o CVE, konserwację itp.

PS: Nigdy nie manipuluj adresami URL jako ciągami!

Kiedy przeglądam kod, zdecydowanie nie chcę ręcznie manipulować adresami URL jako ciągami . Po pierwsze, spójrz, jak skomplikowana jest specyfikacja .

Po drugie, brak / obecność końcowego / poprzedzonego ukośnika ( /) nie powinno powodować zepsucia wszystkiego! Nigdy nie powinieneś robić:

const url = `${baseUrl}/${somePath}`

a zwłaszcza nie:

uri: host + '/' + SAT_SERVICE + '/' + CONSTELLATION + '/',

Z których właśnie spotkałem się w bazie kodu.

Sean Patrick Murphy
źródło