İçeriğe geç
academia.sh

Ders 06 / 20

Yol İşlemleri

Yol parçalarını birleştirme ve çözme, platform kurallarının ayrı sunulması, betiğin kendi konumundan dayanak kurma ve kök dizin dışına çıkışı engelleme.

İçindekiler

Önceki derste bütün dosya adları çıplak yazıldı: olcumler.ndjson, veri/2024-02-07. Bu adlar çalışma dizinine göre çözülür, dolayısıyla aynı program başka bir dizinden çağrıldığında dosyayı bulamaz. Ayrıca ayırıcı karakteri elle yazmak, aracı tek bir platformun yol kurallarına bağlar.

Bu ders yolları veri olarak ele alan yerleşik modülü tanıtır ve iki soruyu yanıtlar: program kendi dosyalarını nereye göre arayacak, dışarıdan gelen bir yol parçası nereye kadar gitmesine izin verilecek?

Birleştirme ve Çözme

node:path modülünün iki temel fonksiyonu vardır ve aralarındaki fark sık karıştırılır.

join, verilen parçaları platformun ayırıcısıyla birbirine ekler ve sonucu normalleştirir; sonuç göreli verilmişse göreli kalır. resolve ise sağdan sola yürüyerek mutlak bir yol üretir; hiçbir parça mutlak değilse çalışma dizinini başa ekler.

// yol.mjs
import path from 'node:path';

console.log('join      :', path.join('veri', '2024-02-07', 'olcumler.ndjson'));
console.log('join + .. :', path.join('veri', '2024-02-07', '..', 'ozet.json'));
console.log('sep       :', JSON.stringify(path.sep));
console.log();

const yol = 'veri/2024-02-07/olcumler.ndjson';
console.log('dirname   :', path.dirname(yol));
console.log('basename  :', path.basename(yol));
console.log('uzantisiz :', path.basename(yol, path.extname(yol)));
console.log('extname   :', path.extname(yol));
console.log('parse     :', path.parse(yol));
node yol.mjs
join      : veri/2024-02-07/olcumler.ndjson
join + .. : veri/ozet.json
sep       : "/"

dirname   : veri/2024-02-07
basename  : olcumler.ndjson
uzantisiz : olcumler
extname   : .ndjson
parse     : {
  root: '',
  dir: 'veri/2024-02-07',
  base: 'olcumler.ndjson',
  ext: '.ndjson',
  name: 'olcumler'
}

İkinci satır normalleştirmeyi gösteriyor: .. parçası bir üst dizine çıkma anlamına gelir ve sonuçta kaybolur. sep değeri çalıştığınız platforma göre değişir; örnekteki eğik çizgi bir Unix türevinde alınmıştır.

Bu fonksiyonların hiçbiri dosya sistemine bakmaz. extname bir dosyanın gerçekten JSON olduğunu söylemez, adının nasıl bittiğini söyler; dirname o dizinin var olduğunu söylemez. Yol işlemleri saf dizgi işlemleridir — Programlama Temelleri kursundaki saf fonksiyon tanımına uyarlar ve bu yüzden sınanmaları kolaydır.

Platform Kuralları Ayrı Sunulur

Modül, çalıştığı platformun kurallarını varsayılan olarak uygular, ama iki kural kümesini de ayrı ayrı erişilebilir tutar.

// yol-platform.mjs
import path from 'node:path';

console.log('bu platform :', path.join('veri', '2024-02-07', 'ozet.json'));
console.log('posix       :', path.posix.join('veri', '2024-02-07', 'ozet.json'));
console.log('win32       :', path.win32.join('veri', '2024-02-07', 'ozet.json'));
console.log();
console.log('win32 kurallariyla "C:\\veri" mutlak mi:', path.win32.isAbsolute('C:\\veri'));
console.log('posix kurallariyla "C:\\veri" mutlak mi:', path.posix.isAbsolute('C:\\veri'));
console.log('posix kurallariyla "/veri"   mutlak mi:', path.posix.isAbsolute('/veri'));
node yol-platform.mjs
bu platform : veri/2024-02-07/ozet.json
posix       : veri/2024-02-07/ozet.json
win32       : veri\2024-02-07\ozet.json

win32 kurallariyla "C:\veri" mutlak mi: true
posix kurallariyla "C:\veri" mutlak mi: false
posix kurallariyla "/veri"   mutlak mi: true

İlk satır, örneğin çalıştırıldığı platformun kurallarını yansıtır; bir Windows makinesinde ters eğik çizgili biçim yazardı. Ayrı kural kümelerinin varlığı, yerel dosya yollarıyla ilgisi olmayan işler için önemlidir: bir arşiv girdisinin veya bir HTTP yolunun ayrıştırılması her platformda aynı sonucu vermelidir; bunun için path.posix doğrudan seçilir.

Adres yolları için ayrıca URL nesnesi vardır ve iki alan karıştırılmamalıdır. Bir istek yolundaki karakter kaçışları path fonksiyonlarının bilmediği kurallara uyar; sunucu tarafında yol ayrıştırması adres nesnesiyle yapılır.

Bir Yoldan Başka Bir Yol Türetmek

Ölçüm toplayıcının sık yaptığı iş, bir girdi dosyasının adından çıktı dosyasının adını üretmektir: olcumler.ndjson okunur, yanına olcumler-ozet.json yazılır. Dizgi birleştirmesiyle yapıldığında bu iş uzantının kaç karakter olduğu varsayımına dayanır ve .ndjson ile .json arasında bozulur.

parse bir yolu alanlarına ayırır, format alanlardan yol kurar. İkisi birlikte kullanıldığında türetme, karakter saymadan yapılır — ancak format çağrısının bir önceliği vardır:

// yol-turetme.mjs
import path from 'node:path';

const kaynak = 'veri/2024-02-07/olcumler.ndjson';
const parcalar = path.parse(kaynak);

// base alani varsa ext ve name yok sayilir; turetmek icin base silinir
const hedef = path.format({ dir: parcalar.dir, name: `${parcalar.name}-ozet`, ext: '.json' });
const yanlis = path.format({ ...parcalar, name: `${parcalar.name}-ozet`, ext: '.json' });

console.log('kaynak     :', kaynak);
console.log('hedef      :', hedef);
console.log('base kalinca:', yanlis);
console.log();

console.log('relative (komsu)  :', path.relative('veri/2024-02-07', 'veri/2024-02-08/olcumler.ndjson'));
console.log('relative (ayni)   :', JSON.stringify(path.relative('veri', 'veri')));
console.log('relative (disari) :', path.relative('veri', 'gizli.txt'));
node yol-turetme.mjs
kaynak     : veri/2024-02-07/olcumler.ndjson
hedef      : veri/2024-02-07/olcumler-ozet.json
base kalinca: veri/2024-02-07/olcumler.ndjson
relative (komsu)  : ../2024-02-08/olcumler.ndjson
relative (ayni)   : ""
relative (disari) : ../gizli.txt

Üçüncü satır tuzağı gösteriyor. format, verilen nesnede base alanı varsa name ve ext alanlarını yok sayar; parse çıktısı base alanını taşıdığı için doğrudan yayılan bir nesne hiç değişmemiş yolu geri verir. Türetme yapılırken base ya verilmez ya da yenisi hesaplanır.

Çıktının ikinci öbeği relative fonksiyonunu tanıtıyor: iki yol arasında, birinciden ikinciye gitmek için izlenecek göreli adımı verir. Üç davranış ayrı ayrı anlamlıdır. Komşu bir dizine giden yol bir üst düzeye çıkıp iner. İki yol aynıysa sonuç boş dizgidir — bir alt öğe değil, yolun kendisi kastedilmiştir. Hedef, tabanın dışında kalıyorsa sonuç .. ile başlar.

Son davranış, bir sonraki bölümdeki kök denetiminin ikinci yazım biçimidir: path.relative(kok, tam) sonucu .. ile başlamıyorsa hedef kökün altındadır. Bu biçim ayırıcı eklemeyi gerektirmez, ancak yol ayrımı büyük–küçük harf duyarsız bir dosya sisteminde her iki biçim de aynı sınırla karşılaşır: dizgi karşılaştırması, dosya sisteminin ad eşleme kuralını bilmez.

Betiğin Kendi Konumu

Bir programın kendi yanındaki dosyaları bulması, çalışma dizinine bağlı kalmamalıdır. ES modüllerinde dayanak import.meta.url değeridir; bu bir dosya adresidir ve dosya sistemi yoluna çevrilmesi gerekir.

// yol-guvenlik.mjs
import path from 'node:path';
import { fileURLToPath } from 'node:url';

// Betiğin kendi konumu: çalışma dizininden bağımsız sabit dayanak
const buDosya = fileURLToPath(import.meta.url);
const buDizin = path.dirname(buDosya);
console.log('betigin dizini (son parca):', path.basename(buDizin));

const kok = path.resolve(buDizin, 'veri');

function guvenliCoz(kullaniciYolu) {
  const tam = path.resolve(kok, kullaniciYolu);
  if (tam !== kok && !tam.startsWith(kok + path.sep)) {
    throw new Error(`kok dizin disi: ${kullaniciYolu}`);
  }
  return path.relative(kok, tam);
}

for (const istek of ['2024-02-07/olcumler.ndjson', './a/../b.json', '../gizli.txt']) {
  try {
    console.log(`${istek.padEnd(28)} -> ${guvenliCoz(istek)}`);
  } catch (hata) {
    console.log(`${istek.padEnd(28)} -> RED: ${hata.message}`);
  }
}

Aynı betik iki farklı dizinden çağrıldığında aynı sonucu verir:

node yol-guvenlik.mjs
betigin dizini (son parca): lab
2024-02-07/olcumler.ndjson   -> 2024-02-07/olcumler.ndjson
./a/../b.json                -> b.json
../gizli.txt                 -> RED: kok dizin disi: ../gizli.txt

İlk satırdaki dizin adı, betiği hangi dizine kaydettiğinize göre değişir; örnekte lab adlı bir dizinden alınmıştır. Önemli olan, komutu kök dizinden veya başka bir yerden çağırdığınızda bu değerin değişmemesidir.

Adresten yola çevirme işi elle yapılamaz. Dosya adresinde boşluk %20 ile kodlanır ve Windows’ta sürücü harfi ek bir kurala uyar; fileURLToPath bu ayrıntıları üstlenir. Ters yönde, bir yolu adrese çevirmek için aynı modüldeki pathToFileURL kullanılır.

Kök Dizin Dışına Çıkmayı Engellemek

Yukarıdaki guvenliCoz fonksiyonu, ölçüm toplayıcının ileride karşılaşacağı bir sorunu şimdiden çözer. Hizmet, istek yolundan gelen bir ad ile dosya açacaksa, o adın belirlenen kök dizinin altında kaldığı doğrulanmalıdır. Doğrulanmazsa ../ dizisi içeren bir istek, kökün dışındaki dosyalara erişebilir.

Doğrulamanın üç adımı vardır ve sırası önemlidir:

  1. Kök dizini mutlak yola çevir.
  2. Kullanıcı yolunu kökün altına çöz. resolve bu adımda .. parçalarını uygular.
  3. Sonucun kök dizine eşit olduğunu veya kök artı ayırıcı ile başladığını doğrula.

Üçüncü adımdaki ayırıcı ayrıntısı gereksiz görünür ama değildir. Yalnızca tam.startsWith(kok) denetlenirse, veri kökü için veri-yedek adlı komşu dizin de denetimi geçer; ayırıcının eklenmesi bu sızıntıyı kapatır.

Bu denetim, dizgi düzeyinde bir denetimdir ve sembolik bağları hesaba katmaz. Kök dizinin içinde dışarıyı gösteren bir sembolik bağ varsa, çözülen yol kökün altında görünürken açılan dosya dışarıda olabilir. Linux’a Giriş kursunda tanıtılan sembolik bağ davranışı burada devreye girer; gerçek yolu almak için dosya sistemine soran realpath çağrısı kullanılır ve denetim onun sonucu üzerinde yinelenir.

Özet

  • join parçaları birleştirip normalleştirir ve göreliliği korur; resolve sağdan sola yürüyerek mutlak yol üretir.
  • Yol fonksiyonları dosya sistemine bakmaz; adın biçimi hakkında konuşurlar, dosyanın varlığı hakkında değil.
  • Modül, iki platformun kurallarını ayrı ayrı sunar; yerel dosya dışındaki yollar için kural kümesi açıkça seçilir.
  • parse ve format ile bir yoldan başkası karakter saymadan türetilir; format, nesnede base alanı varsa name ve ext alanlarını yok sayar.
  • ES modülünde betiğin kendi konumu import.meta.url adresinden fileURLToPath ile elde edilir; bu dayanak çalışma dizininden bağımsızdır.
  • Dışarıdan gelen yol, mutlak köke çözülüp kök artı ayırıcı önekiyle sınanarak denetlenir; sembolik bağlar için gerçek yol ayrıca sorulur.

Sonraki Adım

Buraya kadar dosyalar bir kerede belleğe alındı. On iki satırlık bir dosyada bu sorunsuzdur; günlerce biriken bir ölçüm dosyasında değildir. Sonraki ders veriyi parça parça taşıyan akış soyutlamasını kurar: okunabilir, yazılabilir ve dönüştürücü akışları, bunları birbirine bağlamanın doğru yolunu ve üreticinin tüketiciden hızlı olduğunda devreye giren geri basıncı ele alır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat