د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شي

اې حبره!

زموږ په کار کې، زموږ شرکت ډیری وختونه د مختلف جامد کوډ تحلیل وسیلو (SAST) سره معامله کوي. د بکس څخه بهر دوی ټول په اوسط ډول کار کوي. البته، دا ټول د پروژې او په هغې کې کارول شوي ټیکنالوژیو پورې اړه لري، په بیله بیا دا ټیکنالوژي څومره د تحلیلي قواعدو لخوا پوښل شوي. زما په اند، یو له خورا مهم معیارونو څخه کله چې د SAST وسیلې غوره کول د دې وړتیا ده چې دا ستاسو د غوښتنلیکونو ځانګړتیاو سره تنظیم کړي ، د بیلګې په توګه ، د تحلیل قواعد ولیکئ او بدل کړئ یا لکه څنګه چې دوی ډیری وختونه ویل کیږي ، دودیز پوښتنې.

د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شي

موږ ډیری وختونه چیک مارکس کاروو - یو خورا په زړه پوری او پیاوړی کوډ تحلیل کونکی. پدې مقاله کې به زه د دې لپاره د تحلیلي قواعدو لیکلو زما تجربې په اړه وغږیږم.

فهرست

د ننوتلو

د پیل کولو لپاره ، زه غواړم د چیک مارکس لپاره د پوښتنو لیکلو ځانګړتیاو په اړه په روسیه کې د څو مقالو څخه یو وړاندیز وکړم. دا د 2019 په پای کې په هابري کې د دې سرلیک لاندې خپور شو: "سلام، چیک مارکس!" د چیک مارکس SAST پوښتنې لیکلو څرنګوالی او ښه زیانونه ومومئ.

دا په تفصیل سره معاینه کوي چې څنګه د ځینې ازموینې غوښتنلیک لپاره په CxQL (د چیک مارکس پوښتنې ژبه) کې لومړۍ پوښتنې ولیکئ او د تحلیل قواعد څنګه کار کوي لومړني اصول ښیې.

زه به هغه څه تکرار نه کړم چې په دې کې بیان شوي، که څه هم ځینې مقطعات به شتون ولري. زما په مقاله کې به زه هڅه وکړم چې یو ډول "د ترکیبونو مجموعه" ترتیب کړم، د ځانګړو ستونزو د حلونو لیست چې ما د چک مارکس سره زما د کار په جریان کې ورسره مخامخ شوی و. ما باید د دې ډیری ستونزو په اړه خپل دماغ ریک کړم. ځینې ​​​​وختونه په اسنادو کې کافي معلومات نه و، او ځینې وختونه دا ستونزمنه وه چې پوه شي چې څنګه باید څه وکړي. زه امید لرم چې زما تجربه او بې خوبه شپې به بې ګټې نه وي ، او دا "د ګمرک پوښتنو ترکیبونو ټولګه" به تاسو څو ساعته یا څو عصبي حجرې خوندي کړي. نو، راځئ چې پیل وکړو!

د قواعدو په اړه عمومي معلومات

لومړی، راځئ چې یو څو بنسټیز مفکورې او د قواعدو سره د کار کولو پروسې ته وګورو، د ښه پوهیدو لپاره چې راتلونکی څه پیښیږي. او دا هم ځکه چې اسناد پدې اړه څه نه وايي یا په جوړښت کې خورا پراخه شوي ، کوم چې خورا اسانه ندي.

  1. مقررات د سکین کولو پرمهال پلي کیږي په پیل کې د ټاکل شوي ټاکل شوي (فعال قواعدو سیټ) پورې اړه لري. تاسو کولی شئ غیر محدود شمیر پری سیټونه رامینځته کړئ ، او دقیقا د دوی جوړښت څنګه ستاسو د پروسې ځانګړتیاو پورې اړه لري. تاسو کولی شئ دوی د ژبې له مخې ګروپ کړئ یا د هرې پروژې لپاره پری سیټونه غوره کړئ. د فعالو مقرراتو شمیر د سکینګ سرعت او دقت اغیزه کوي.

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد چیک مارکس انٹرفیس کې د پریزیټ تنظیم کول

  2. مقررات د CxAuditor په نوم په یوه ځانګړي وسیله کې تدوین شوي. دا یو ډیسټاپ غوښتنلیک دی چې د چیک مارکس چلونکي سرور سره وصل کیږي. دا وسیله د عملیاتو دوه طریقې لري: د مقرراتو ترمیم کول او د دمخه ترسره شوي سکین پایلې تحلیل کول.

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد CxAudit انٹرفیس

  3. په چیک مارکس کې قواعد د ژبې له مخې ویشل شوي، دا دا ده چې هره ژبه د خپلو پوښتنو مجموعه لري. دلته ځینې عمومي قواعد هم شتون لري چې د ژبې په پام کې نیولو پرته پلي کیږي، دا تش په نامه اساسي پوښتنې دي. د ډیری برخې لپاره، اساسي پوښتنې د هغو معلوماتو لټون کول شامل دي چې نور قواعد یې کاروي.

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد ژبې له مخې د قواعدو ویش

  4. مقررات "د اجرا وړ" او "غیر اجرا وړ" (اعدام شوي او نه اجرا شوي) دي. زما په نظر سم نوم نه دی، مګر دا هغه څه دي. لاندینۍ کرښه دا ده چې د "عملي کولو وړ" مقرراتو اجرا کولو پایله به په UI کې د سکین کولو پایلو کې ښودل شي، او "غیر اجرا وړ" قواعد یوازې د دوی پایلې په نورو غوښتنو کې کارولو لپاره اړین دي (په حقیقت کې، دا یوازې یو فعالیت دی. ).

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد جوړولو پر مهال د قاعدې ډول معلومول

  5. تاسو کولی شئ نوي قواعد رامینځته کړئ یا موجوده ضمیمه / بیا ولیکئ. د یوې قاعدې د بیا لیکلو لپاره، تاسو اړتیا لرئ چې دا په ونه کې ومومئ، ښیې کلیک وکړئ او د ډراپ-ډاون مینو څخه "اووررایډ" غوره کړئ. دلته دا مهمه ده چې یاد ولرئ چې نوي مقررات په پیل کې په پریزټونو کې ندي شامل شوي او فعال ندي. د دوی کارولو پیل کولو لپاره تاسو اړتیا لرئ په وسیله کې د "پریسیټ مدیر" مینو کې فعال کړئ. بیا لیکل شوي مقررات خپل تنظیمات ساتي، دا دی، که چیرې قاعده فعاله وي، دا به همداسې پاتې وي او سمدلاسه به پلي شي.

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد پریزیټ مدیر انٹرفیس کې د نوي قاعدې بیلګه

  6. د اعدام په جریان کې، د غوښتنو "ونې" جوړیږي، کوم چې په څه پورې اړه لري. هغه مقررات چې معلومات راټولوي لومړی اجرا کیږي، او هغه څوک چې دوی کاروي. د اعدام پایله زیرمه شوې ، نو که چیرې دا ممکنه وي چې د موجوده قاعدې پایلې وکاروئ ، نو دا به غوره وي چې دا کار وکړئ ، دا به د سکین کولو وخت کم کړي.

  7. قواعد په مختلفو کچو پلي کیدی شي:

  • د ټول سیسټم لپاره - د هرې پروژې سکین کولو لپاره به وکارول شي

  • د ټیم په کچه (ټیم) - به یوازې په ټاکل شوي ټیم کې د پروژو سکین کولو لپاره وکارول شي.

  • د پروژې په کچه - به په یوه ځانګړې پروژه کې پلي شي

    د چیک مارکس لپاره قواعد لیکلو څرنګوالی پرته لدې چې لیونی شيد هغه کچې معلومول چې په کوم کې به قاعده پلي شي

د پیل کونکو لپاره "لغت".

او زه به د یو څو شیانو سره پیل وکړم چې زما د پوښتنو لامل شوي ، او زه به یو شمیر تخنیکونه هم وښیم چې ژوند به د پام وړ ساده کړي.

د لیستونو سره عملیات

- вычитание одного из другого (list2 - list1)
* пересечение списков (list1 * list2)
+ сложение списков (list1 + list2)

& (логическое И) - объединяет списки по совпадению (list1 & list2), аналогично пересечению (list1 * list2)
| (логическое ИЛИ) - объединяет списки по широкому поиску (list1 | list2)

Со списками не работает:  ^  &&  ||  %  / 

ټول موندل شوي توکي

په سکین شوي ژبه کې، تاسو کولی شئ د ټولو عناصرو لیست ترلاسه کړئ چې چیک مارکس پیژندلي دي (تارونه، دندې، ټولګي، میتودونه، او نور). دا د شیانو ځینې ځای دی چې له لارې لاسرسی کیدی شي All. دا د یو ځانګړي نوم سره د یو شی لټون کول دي searchMe، تاسو کولی شئ د مثال په توګه په ټولو موندل شوي شیانو کې د نوم په واسطه لټون وکړئ:

// Такой запрос выдаст все элементы
result = All;

// Такой запрос выдаст все элементы, в имени которых присутствует “searchMe“
result = All.FindByName("searchMe");

مګر، که تاسو په بله ژبه کې لټون کولو ته اړتیا لرئ چې د کوم دلیل لپاره په سکین کې شامل نه و (د مثال په توګه، په Android پروژه کې گرووی)، تاسو کولی شئ زموږ د اعتراض ځای د متغیر له لارې پراخ کړئ:

result = AllMembers.All.FindByName("searchMe");

د جریان تحلیل لپاره دندې

دا افعال په ډیری قواعدو کې کارول کیږي او دلته د دوی د څه معنی یوه کوچنۍ غلا پاڼه ده:

// Какие данные second влияют на first.
// Другими словами - ТО (second) что влияет на  МЕНЯ (first).
result = first.DataInfluencedBy(second);

// Какие данные first влияют на second.
// Другими словами - Я (first) влияю на ТО (second).
result = first.DataInfluencingOn(second);

د فایل نوم / لاره ترلاسه کول

ډیری ځانګړتیاوې شتون لري چې د یوې پوښتنې پایلو څخه ترلاسه کیدی شي (د فایل نوم چې په هغه کې ننوتل موندل شوي، تار، او نور)، مګر اسناد دا نه وايي چې څنګه یې ترلاسه کول او کارول. نو، د دې کولو لپاره، تاسو اړتیا لرئ د LinePragma ملکیت ته لاسرسی ومومئ او هغه شیان چې موږ ورته اړتیا لرو د هغې دننه واقع وي:

// Для примера найдем все методы
CxList methods = Find_Methods();

// В методах найдем по имени метод scope
CxList scope = methods.FindByName("scope");

// Таким образом можо получить путь к файлу
string current_filename = scope.GetFirstGraph().LinePragma.FileName;

// А вот таким - строку, где нашлось срабатывание
int current_line = scope.GetFirstGraph().LinePragma.Line;

// Эти параметры можно использовать по разному
// Например получить все объекты в файле
CxList inFile = All.FindByFileName(current_filename);

// Или найти что происходит в конкретной строке
CxList inLine = inFile.FindByPosition(current_line);

دا په پام کې نیولو سره ارزښت لري FileName په حقیقت کې فایل ته لاره لري، ځکه چې موږ میتود کارولی GetFirstGraph.

د اعدام پایله

د CxQL دننه یو ځانګړی متغیر شتون لري result، کوم چې ستاسو د لیکل شوي قانون پلي کولو پایله بیرته راوړي. دا سمدلاسه پیل کیږي او تاسو کولی شئ په دې کې منځمهاله پایلې ولیکئ، د کار کولو په څیر یې بدلول او پاکول. مګر، که چیرې د دې متغیر لپاره کومه دنده شتون ونلري یا د قاعدې دننه فعالیت وکړي return- د اعدام پایله به تل صفر وي.

لاندې پوښتنه به د اعدام په پایله کې موږ ته هیڅ شی نه راګرځوي او تل به خالي وي:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

مګر ، د جادو متغیر پایلې ته د اعدام پایله ټاکلو سره ، موږ به وګورو چې دا زنګ موږ ته څه راګرځي:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

// Выводим, как результат выполнения правила
result = libraries

// Или еще короче
result = All.FindByName("foo");

د نورو قواعدو پایلې کارول

په چیک مارکس کې قواعد په منظم پروګرامینګ ژبه کې د فعالیتونو سره ورته بلل کیدی شي. کله چې یو قواعد ولیکئ، تاسو کولی شئ د نورو پوښتنو پایلې په ښه توګه وکاروئ. د مثال په توګه ، هر ځل په کوډ کې د ټولو میتودونو زنګونو لټون کولو ته اړتیا نشته ، یوازې مطلوب قانون ته زنګ ووهئ:

// Получаем результат выполнения другого правила
CxList methods = Find_Methods();

// Ищем внутри метод foo. 
// Второй параметр false означает, что ищем без чувствительности к регистру
result = methods.FindByShortName("foo", false);

دا طریقه تاسو ته اجازه درکوي کوډ لنډ کړئ او د پام وړ د قانون اجرا کولو وخت کم کړئ.

ستونزه حل کول

ننوتل

کله چې د وسیلې سره کار کوئ ، ځینې وختونه دا امکان نلري چې سمدلاسه مطلوب پوښتنه ولیکئ او تاسو باید تجربه وکړئ ، مختلف اختیارونه هڅه وکړئ. د داسې یوې قضیې لپاره، وسیله د ننوتلو چمتو کوي، کوم چې په لاندې ډول ویل کیږي:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Формируем строку и отправляем в лог
cxLog.WriteDebugMessage (“number of DOM elements =” + All.Count);

مګر دا د یادولو وړ ده چې دا طریقه یوازې د ننوتلو په توګه مني تار، نو دا به امکان ونلري چې د لومړي عملیاتو په پایله کې د موندل شوي عناصرو بشپړ لیست ښکاره کړي. دوهم اختیار، چې د ډیبګ کولو لپاره کارول کیږي، د وخت په وخت کې د جادو متغیر ته ټاکل کیږي result د پوښتنې پایله او وګورئ چې څه پیښیږي. دا طریقه ډیره اسانه نه ده؛ تاسو اړتیا لرئ ډاډ ترلاسه کړئ چې په کوډ کې د دې سره هیڅ ډول بدلونونه یا عملیات شتون نلري. result یا په ساده ډول لاندې کوډ تبصره کړئ. یا تاسو کولی شئ ، زما په څیر ، د چمتو شوي قاعدې څخه ډیری ورته تلیفونونه لرې کول هیر کړئ او حیران شئ چې ولې هیڅ کار نه کوي.

یوه ډیره اسانه لاره دا ده چې میتود ته زنګ ووهئ return د اړین پیرامیټر سره. په دې حالت کې، د حاکمیت اجرا به پای ته ورسیږي او موږ به وګورو چې د هغه څه په پایله کې چې موږ لیکلي وو:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Выводим результат выполнения
return toLog

//Все, что написано дальше не будет выполнено
result = All.DataInfluencedBy(toLog)

د ننوتلو ستونزه

داسې شرایط شتون لري کله چې تاسو د CxAudit وسیلې ته لاسرسی نشئ کولی (کوم چې د قواعدو لیکلو لپاره کارول کیږي). د دې لپاره ډیری دلایل شتون لري ، پشمول د حادثې ، ناڅاپه وینډوز تازه معلومات ، BSOD او نور غیر متوقع حالتونه چې زموږ له کنټرول څخه بهر دي. په دې حالت کې، ځینې وختونه په ډیټابیس کې یوه ناببره ناسته شتون لري، کوم چې تاسو د بیا ننوتلو مخه نیسي. د دې د حل کولو لپاره، تاسو اړتیا لرئ څو پوښتنې پرمخ بوځي:

د چک مارکس لپاره مخکې له 8.6:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;

د 8.6 وروسته د چیک مارکس لپاره:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM LoggedinUser WHERE (ClientType = 'Audit');
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE (ClientType = 'Audit');

د لیکلو قواعد

اوس موږ تر ټولو په زړه پورې برخې ته ورسیږو. کله چې تاسو په CxQL کې د قواعدو لیکلو پیل کوئ، هغه څه چې تاسو ډیری وختونه کمښت لرئ دومره اسناد نه دي لکه څنګه چې د ځینې ستونزو حل کولو ژوندي مثالونه او په عمومي توګه د پوښتنو عملیاتي پروسې تشریح کول.

زه به هڅه وکړم چې د هغو کسانو لپاره ژوند یو څه اسانه کړم چې د پوښتنو په ژبه کې ډوب کول پیل کړي او د ځینې ستونزو حل کولو لپاره د ګمرک پوښتنو کارولو ډیری مثالونه وړاندې کړم. ځینې ​​یې خورا عمومي دي او ستاسو په شرکت کې په عملي ډول پرته له کوم بدلون څخه کارول کیدی شي ، نور یې خورا ځانګړي دي ، مګر دا ستاسو د غوښتنلیکونو ځانګړتیاو سره سم د کوډ بدلولو سره هم کارول کیدی شي.

نو، دلته هغه ستونزې دي چې موږ ډیری وختونه ورسره مخ یو:

دنده: د قاعدې د اجرا کولو په پایلو کې ډیری جریانونه شتون لري او یو یې د بل ځړول دي، تاسو باید یو یې پریږدئ.

د حل لاره: په حقیقت کې، ځینې وختونه چیک مارکس د ډیټا ډیری جریان ښیې چې ممکن د نورو لنډه نسخه وي. د داسې قضیو لپاره یو ځانګړی میتود شتون لري د فلو کمول. د پیرامیټر پورې اړه لري، دا به ترټولو لنډ یا اوږد جریان غوره کړي:

// Оставить только длинные Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow);

// Оставить только короткие Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow);

دنده: د حساسو معلوماتو لیست پراخ کړئ چې وسیله یې غبرګون کوي

د حل لاره: چیک مارکس بنسټیز قواعد لري، چې پایلې یې د ډیرو نورو پوښتنو لخوا کارول کیږي. د دې ځینې مقرراتو ضمیمه کولو سره ستاسو غوښتنلیک ته ځانګړي ډیټا سره ، تاسو کولی شئ سمدلاسه خپلې سکین پایلې ته وده ورکړئ. لاندې ستاسو د پیل کولو لپاره یو مثال قانون دی:

عمومي_محرم_سرغړونې_لست

راځئ چې څو متغیرونه اضافه کړو چې زموږ په غوښتنلیک کې د حساس معلوماتو ذخیره کولو لپاره کارول کیږي:

// Получаем результат выполнения базового правила
result = base.General_privacy_violation_list();

// Ищем элементы, которые попадают под простые регулярные выражения. Можно дополнить характерными для вас паттернами.
CxList personalList = All.FindByShortNames(new List<string> {
	"*securityToken*", "*sessionId*"}, false);

// Добавляем к конечному результату
result.Add(personalList);

دنده: د پاسورډونو سره د متغیرونو لیست پراخ کړئ

د حل لاره: زه به سمدلاسه وړاندیز وکړم چې په کوډ کې د پاسورډونو تعریف کولو لپاره لومړني قاعدې ته پاملرنه وکړئ او پدې کې د متغیر نومونو لیست اضافه کړئ چې معمولا ستاسو په شرکت کې کارول کیږي.

د پټنوم_د محرمیت_سرغړونې_لست

CxList allStrings = All.FindByType("String"); 
allStrings.Add(All.FindByType(typeof(StringLiteral))); 
allStrings.Add(Find_UnknownReference());
allStrings.Add(All.FindByType(typeof (Declarator)));
allStrings.Add(All.FindByType(typeof (MemberAccess)));
allStrings.Add(All.FindByType(typeof(EnumMemberDecl))); 
allStrings.Add(Find_Methods().FindByShortName("get*"));

// Дополняем дефолтный список переменных
List < string > pswdIncludeList = new List<string>{"*password*", "*psw", "psw*", "pwd*", "*pwd", "*authKey*", "pass*", "cipher*", "*cipher", "pass", "adgangskode", "benutzerkennwort", "chiffre", "clave", "codewort", "contrasena", "contrasenya", "geheimcode", "geslo", "heslo", "jelszo", "kennwort", "losenord", "losung", "losungswort", "lozinka", "modpas", "motdepasse", "parol", "parola", "parole", "pasahitza", "pasfhocal", "passe", "passord", "passwort", "pasvorto", "paswoord", "salasana", "schluessel", "schluesselwort", "senha", "sifre", "wachtwoord", "wagwoord", "watchword", "zugangswort", "PAROLACHIAVE", "PAROLA CHIAVE", "PAROLECHIAVI", "PAROLE CHIAVI", "paroladordine", "verschluesselt", "sisma",
                "pincode",
								"pin"};
								
List < string > pswdExcludeList = new List<string>{"*pass", "*passable*", "*passage*", "*passenger*", "*passer*", "*passing*", "*passion*", "*passive*", "*passover*", "*passport*", "*passed*", "*compass*", "*bypass*", "pass-through", "passthru", "passthrough", "passbytes", "passcount", "passratio"};

CxList tempResult = allStrings.FindByShortNames(pswdIncludeList, false);
CxList toRemove = tempResult.FindByShortNames(pswdExcludeList, false);
tempResult -= toRemove;
tempResult.Add(allStrings.FindByShortName("pass", false));

foreach (CxList r in tempResult)
{
	CSharpGraph g = r.data.GetByIndex(0) as CSharpGraph;
	if(g != null && g.ShortName != null && g.ShortName.Length < 50)
	{
		result.Add(r);
	}
}

دنده: کارول شوي چوکاټونه اضافه کړئ چې د چیک مارکس لخوا نه ملاتړ کیږي

د حل لاره: په چیک مارکس کې ټولې پوښتنې د ژبې له مخې ویشل شوي، نو تاسو اړتیا لرئ چې د هرې ژبې لپاره قواعد اضافه کړئ. لاندې د داسې قواعدو ځینې مثالونه دي.

که کتابتونونه وکارول شي چې د معیاري فعالیت بشپړولو یا ځای په ځای کړي، دوی په اسانۍ سره په اساسي قانون کې اضافه کیدی شي. بیا هرڅوک چې دا کاروي سمدلاسه به د نوي معرفي کولو په اړه زده کړي. د مثال په توګه، په Android کې د ننوتلو لپاره کتابتونونه Timber او Loggi دي. په لومړني بسته کې، د غیر سیسټم تلیفونونو پیژندلو لپاره هیڅ مقررات شتون نلري، نو که چیرې پاسورډ یا سیشن پیژندونکی لاګ ته راشي، موږ به یې په اړه نه پوهیږو. راځئ هڅه وکړو چې د چک مارکس قواعدو ته د دې ډول میتودونو تعریفونه اضافه کړو.

د ازموینې کوډ مثال چې د ننوتلو لپاره د تیمبر کتابتون کاروي:

package com.death.timberdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Timber.e("Error Message");
        Timber.d("Debug Message");

        Timber.tag("Some Different tag").e("And error message");
    }
}

او دلته د چیک مارکس لپاره د غوښتنې یوه بیلګه ده ، کوم چې تاسو ته اجازه درکوي د غوښتنلیک څخه ډیټا لپاره د وتلو نقطې په توګه د تیمبر میتودونو زنګ وهلو تعریف اضافه کړئ:

دAndroidOutputs ومومئ

// Получаем результат выполнения базового правила
result = base.Find_Android_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
CxList timber = All.FindByExactMemberAccess("Timber.*") +
    All.FindByShortName("Timber").GetMembersOfTarget();

// Добавляем к конечному результату
result.Add(timber);

او تاسو کولی شئ د ګاونډي قانون ته هم اضافه کړئ، مګر دا په مستقیم ډول په Android کې د ننوتلو سره تړاو لري:

دAndroidLog_Outputs ومومئ

// Получаем результат выполнения базового правила
result = base.Find_Android_Log_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
result.Add(
  All.FindByExactMemberAccess("Timber.*") +
  All.FindByShortName("Timber").GetMembersOfTarget()
);

همدارنګه، که د Android غوښتنلیکونه وکاروي د کار مدیر د غیر متناسب کار لپاره، دا یو ښه نظر دی چې د دندې څخه د معلوماتو ترلاسه کولو لپاره میتود اضافه کولو سره چیک مارکس ته د دې په اړه خبر ورکړئ. getInputData:

انډرایډ ریډ ومومئ

// Получаем результат выполнения базового правила
result = base.Find_Android_Read();

// Дополняем вызовом функции getInputData, которая используется в WorkManager
CxList getInputData = All.FindByShortName("getInputData");

// Добавляем к конечному результату
result.Add(getInputData.GetMembersOfTarget());

دنده: د iOS پروژو لپاره په لیست کې د حساس معلوماتو لټون

د حل لاره: iOS ډیری وختونه د مختلف متغیرونو او ارزښتونو ذخیره کولو لپاره د .plist توسیع سره ځانګړي فایلونه کاروي. په دې فایلونو کې د پاسورډونو، ټوکنونو، کیليونو او نورو حساسو معلوماتو ذخیره کولو سپارښتنه نه کیږي، ځکه چې دوی پرته له کومې ستونزې څخه د وسیله څخه ایستل کیدی شي.

د Plist فایلونه داسې ځانګړتیاوې لري چې د سترګو لپاره ښکاره ندي، مګر د چیک مارکس لپاره مهم دي. راځئ چې یو قاعده ولیکئ چې هغه ډیټا وپلټئ چې موږ ورته اړتیا لرو او موږ ته ووایی که چیرې پاسورډونه یا ټیکونه په کوم ځای کې ذکر شوي وي.

د داسې فایل یوه بیلګه، کوم چې د پس منظر خدمت سره د اړیکو لپاره نښه لري:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>DeviceDictionary</key>
	<dict>
		<key>phone</key>
		<string>iPhone 6s</string>
	</dict>
	<key>privatekey</key>
	<string>MIICXAIBAAKBgQCqGKukO1De7zhZj6+</string>
</dict>
</plist>

او د چیک مارکس لپاره یو قاعده ، کوم چې ډیری لنډیزونه لري چې د لیکلو پرمهال باید په پام کې ونیول شي:

// Используем результат выполнения правила по поиску файлов plist, чтобы уменьшить время работы правила и 
CxList plist = Find_Plist_Elements();

// Инициализируем новую переменную
CxList dictionarySettings = All.NewCxList();

// Теперь добавим поиск всех интересующих нас значений. В дальнейшем можно расширять этот список.
// Для поиска значений, как ни странно, используется FindByMemberAccess - поиск обращений к методам. Второй параметр внутри функции, false, означает, что поиск нечувствителен к регистру
dictionarySettings.Add(plist.FindByMemberAccess("privatekey", false));
dictionarySettings.Add(plist.FindByMemberAccess("privatetoken", false));

// Для корректного поиска из-за особенностей структуры plist - нужно искать по типу "If statement"
CxList ifStatements = plist.FindByType(typeof(IfStmt));

// Добавляем в результат, перед этим получив родительский узел - для правильного отображения
result = dictionarySettings.FindByFathers(ifStatements);

دنده: په XML کې د معلوماتو موندل

د حل لاره: چیک مارکس د XML سره کار کولو او د ارزښتونو ، ټاګونو ، ځانګړتیاو او نورو لټون کولو لپاره خورا اسانه دندې لري. مګر، له بده مرغه، په اسنادو کې یوه تېروتنه وه چې له امله یې یوه بیلګه کار نه کوي. د دې حقیقت سره سره چې دا نیمګړتیا د اسنادو په وروستي نسخه کې له مینځه وړل شوې، محتاط اوسئ که تاسو د اسنادو پخوانۍ نسخې کاروئ.

دلته د اسنادو څخه یو غلط مثال دی:

// Код работать не будет
result = All.FindXmlAttributesByNameAndValue("*.app", 8, “id”, "error- section", false, true);

د اعدام د هڅو په پایله کې، موږ به یوه تېروتنه ترلاسه کړو All داسې کومه طریقه شتون نلري ... او دا ریښتیا ده، ځکه چې د XML سره کار کولو لپاره د فنکشن کارولو لپاره یو ځانګړی، جلا جلا ځای شتون لري - cxXPath. دا هغه څه دي چې سمه پوښتنه په Android کې د داسې ترتیب موندلو په څیر ښکاري چې د HTTP ترافیک کارولو ته اجازه ورکوي:

// Правильный вариант с использованием cxXPath
result = cxXPath.FindXmlAttributesByNameAndValue("*.xml", 8, "cleartextTrafficPermitted", "true", false, true);

راځئ چې دا په یو څه نور تفصیل سره وګورو، ځکه چې د ټولو دندو لپاره ترکیب یو شان دی، وروسته له دې چې تاسو یو معلوم کړئ، نو تاسو اړتیا لرئ هغه یو غوره کړئ چې تاسو ورته اړتیا لرئ. نو، په ترتیب سره د پیرامیټونو مطابق:

  • "*.xml"- د لټون کولو لپاره د فایلونو ماسک

  • 8 - د هغه ژبې id چې د هغې لپاره قاعده پلي کیږي

  • "cleartextTrafficPermitted"- په xml کې د ځانګړتیا نوم

  • "true" - د دې خاصیت ارزښت

  • false - د لټون پرمهال د منظم بیان کارول

  • true - پدې معنی چې لټون به د قضیې په پام کې نیولو سره ترسره شي، دا د قضیې غیر حساس دی

د مثال په توګه، موږ یو قاعده کارولې چې غلط پیژني، د امنیت له نظره، په Android کې د شبکې اتصال ترتیبات چې د HTTP پروتوکول له لارې د سرور سره ارتباط ته اجازه ورکوي. د ترتیب بیلګه چې یو خاصیت لري cleartextTrafficPermitted د معنی سره true:

<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="true">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

دنده: پایلې د فایل نوم / لارې لخوا محدود کړئ

د حل لاره: د Android لپاره د ګرځنده اپلیکیشن پراختیا پورې اړوند یوې لویې پروژې کې ، موږ د قاعدې غلط مثبتونو سره مخ شو چې د ګډوډي ترتیب ټاکي. حقیقت دا دی چې د بکس څخه بهر قاعده په فایل کې لټون کوي build.gradle یو ترتیب چې د غوښتنلیک د خوشې کولو نسخې لپاره د مغشوش قواعد پلي کولو مسؤلیت لري.

مګر په لویو پروژو کې ځینې وختونه د ماشومانو فایلونه شتون لري build.gradle، کوم چې په پروژه کې شامل کتابتونونو ته مراجعه کوي. ځانګړتیا دا ده چې حتی که دا فایلونه د ګډوډۍ اړتیا نه په ګوته کوي ، د والدین اسمبلۍ فایل تنظیمات به د تالیف پرمهال پلي شي.

په دې توګه، دنده دا ده چې د ماشوم فایلونو کې محرکونه پرې کړي چې په کتابتونونو پورې اړه لري. دوی کولی شي د کرښې شتون لخوا وپیژندل شي apply 'com.android.library'.

د فایل څخه کوډ مثال build.gradle، کوم چې د ګډوډۍ اړتیا ټاکي:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        ...
    }

    buildTypes {
        release {
            minifyEnabled true
            ...
        }
    }
}

dependencies {
  ...
}

د فایل بیلګه build.gradle د هغه کتابتون لپاره چې په پروژه کې شامل دي چې دا ترتیب نلري:

apply plugin: 'android-library'

dependencies {
  compile 'com.android.support:support-v4:18.0.+'
}

android {
  compileSdkVersion 14
  buildToolsVersion '17.0.0'
  ...
}

او د چیک مارکس لپاره اصول:

ProGuardObfuscationNotInUse

// Поиск метода release среди всех методов в Gradle файлах
CxList releaseMethod = Find_Gradle_Method("release");

// Все объекты из файлов build.gradle
CxList gradleBuildObjects = Find_Gradle_Build_Objects();

// Поиск того, что находится внутри метода "release" среди всех объектов из файлов build.gradle
CxList methodInvokesUnderRelease = gradleBuildObjects.FindByType(typeof(MethodInvokeExpr)).GetByAncs(releaseMethod);

// Ищем внутри gradle-файлов строку "com.android.library" - это значит, что данный файл относится к библиотеке и его необходимо исключить из правила
CxList android_library = gradleBuildObjects.FindByName("com.android.library");

// Инициализация пустого массива
List<string> libraries_path = new List<string> {};

// Проходим через все найденные "дочерние" файлы
foreach(CxList library in android_library)
{
    // Получаем путь к каждому файлу
	string file_name_library = library.GetFirstGraph().LinePragma.FileName;
    
    // Добавляем его в наш массив
	libraries_path.Add(file_name_library);
}

// Ищем все вызовы включения обфускации в релизных настройках
CxList minifyEnabled = methodInvokesUnderRelease.FindByShortName("minifyEnabled");

// Получаем параметры этих вызовов
CxList minifyValue = gradleBuildObjects.GetParameters(minifyEnabled, 0);

// Ищем среди них включенные
CxList minifyValueTrue = minifyValue.FindByShortName("true");

// Немного магии, если не нашли стандартным способом :D
if (minifyValueTrue.Count == 0) {
	minifyValue = minifyValue.FindByAbstractValue(abstractValue => abstractValue is TrueAbstractValue);
} else {
    // А если всё-таки нашли, то предыдущий результат и оставляем
	minifyValue = minifyValueTrue;	
}

// Если не нашлось таких методов
if (minifyValue.Count == 0)
{
    // Для более корректного отображения места срабатывания в файле ищем или buildTypes или android
	CxList tempResult = All.NewCxList();
	CxList buildTypes = Find_Gradle_Method("buildTypes");
	if (buildTypes.Count > 0) {
		tempResult = buildTypes;
	} else {
		tempResult = Find_Gradle_Method("android");
	}
	
	// Для каждого из найденных мест срабатывания проходим и определяем, дочерний или основной файлы сборки
	foreach(CxList res in tempResult)
	{
        // Определяем, в каком файле был найден buildType или android методы
		string file_name_result = res.GetFirstGraph().LinePragma.FileName;
        
        // Если такого файла нет в нашем списке "дочерних" файлов - значит это основной файл и его можно добавить в результат
		if (libraries_path.Contains(file_name_result) == false){
			result.Add(res);
		}
	}
}

دا طریقه خورا نړیواله او ګټوره کیدی شي نه یوازې د Android غوښتنلیکونو لپاره، بلکې د نورو قضیو لپاره هم کله چې تاسو اړتیا لرئ دا معلومه کړئ چې پایله د یو ځانګړي فایل پورې اړه لري.

دنده: د دریمې ډلې کتابتون لپاره ملاتړ اضافه کړئ که چیرې ترکیب په بشپړ ډول ملاتړ نه وي

د حل لاره: د مختلف چوکاټونو شمیر چې د کوډ لیکلو په پروسه کې کارول کیږي په ساده ډول د چارټونو څخه بهر دی. البته، چیک مارکس تل د دوی د شتون په اړه نه پوهیږي، او زموږ دنده دا ده چې دا زده کړو چې پوه شي چې ځینې میتودونه په ځانګړې توګه د دې چوکاټ سره تړاو لري. ځینې ​​​​وختونه دا د دې حقیقت له امله پیچلي کیږي چې چوکاټونه د فعالیت نومونه کاروي کوم چې خورا عام دي او دا ناشونې ده چې د ځانګړي کتابتون سره د ځانګړي تلیفون اړیکه په واضح ډول وټاکي.

مشکل دا دی چې د داسې کتابتونونو ترکیب تل په سمه توګه نه پیژندل کیږي او تاسو باید تجربه وکړئ ترڅو د لوی شمیر غلط مثبتونو ترلاسه کولو څخه مخنیوی وکړئ. د سکین کولو دقت ته وده ورکولو او ستونزه حل کولو لپاره ډیری اختیارونه شتون لري:

  • لومړی اختیار، موږ د ډاډ لپاره پوهیږو چې کتابتون په یوه ځانګړې پروژه کې کارول کیږي او کولی شي د ټیم په کچه قاعده پلي کړي. مګر که چیرې ټیم پریکړه وکړي چې مختلف چلند غوره کړي یا ډیری کتابتونونه وکاروي چې پکې د فعالیت نومونه تیریږي ، موږ کولی شو د ډیری غلط مثبتونو خورا خوندور عکس ترلاسه کړو.

  • دوهم اختیار د فایلونو لټون کول دي په کوم کې چې کتابتون په روښانه ډول وارد شوی. د دې طریقې سره، موږ ډاډه یو چې هغه کتابتون چې موږ ورته اړتیا لرو په دې فایل کې کارول کیږي.

  • او دریم انتخاب دا دی چې پورتنۍ دوه لارې په ګډه وکاروئ.

د مثال په توګه، راځئ یو کتابتون وګورو چې په تنګ حلقو کې ښه پیژندل شوی هوښیاره د سکالا پروګرام کولو ژبې لپاره، یعنې فعالیت د لفظي ارزښتونو جلا کول. په عموم کې، د SQL پوښتنې ته د پیرامیټونو لیږدولو لپاره، تاسو باید آپریټر وکاروئ $، کوم چې ډیټا په مخکینۍ SQL پوښتنې کې ځای په ځای کوي. دا په حقیقت کې په جاوا کې د چمتو شوي بیان مستقیم انلاګ دی. مګر ، که تاسو اړتیا لرئ په متحرک ډول د SQL پوښتنې رامینځته کړئ ، د مثال په توګه ، که تاسو اړتیا لرئ د میز نومونه تیر کړئ ، تاسو کولی شئ آپریټر وکاروئ #$، کوم چې به مستقیم معلومات په پوښتنې کې ځای په ځای کړي (تقریبا د سټینګ کنټینیشن په څیر).

نمونه کوډ:

// В общем случае - значения, контролируемые пользователем
val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOption

چیک مارکس لا تر اوسه نه پوهیږي چې څنګه د Splicing Literal Values ​​او skips آپریټرانو کارول کشف کړي #$، نو راځئ هڅه وکړو چې دا د احتمالي SQL انجیکشنونو پیژندلو لپاره ښوونه وکړو او په کوډ کې سم ځایونه روښانه کړو:

// Находим все импорты
CxList imports = All.FindByType(typeof(Import));

// Ищем по имени, есть ли в импортах slick
CxList slick = imports.FindByShortName("slick");

// Некоторый флаг, определяющий, что импорт библиотеки в коде присутствует
// Для более точного определения - можно применить подход с именем файла
bool not_empty_list = false;
foreach (CxList r in slick)
{
    // Если встретили импорт, считаем, что slick используется
	not_empty_list = true;
}

if (not_empty_list) {
    // Ищем вызовы, в которые передается SQL-строка
	CxList sql = All.FindByShortName("sql");
	sql.Add(All.FindByShortName("sqlu"));
	
	// Определяем данные, которые попадают в эти вызовы
	CxList data_sql = All.DataInfluencingOn(sql);
	
	// Так как синтакис не поддерживается, можно применить подход с регулярными выражениями
	// RegExp стоит использовать крайне осторожно и не применять его на большом количестве данных, так как это может сильно повлиять на производительность
	CxList find_possible_inj = data_sql.FindByRegex(@"#$", true, true, true);

    // Избавляемся от лишних срабатываний, если они есть и выводим в результат
	result = find_possible_inj.FindByType(typeof(BinaryExpr));
}

دنده: د خلاصې سرچینې کتابتونونو کې د کارول شوي زیان منونکي افعال لپاره لټون وکړئ

د حل لاره: ډیری شرکتونه په پرمختللو غوښتنلیکونو کې د کتابتونونو د زیان منونکي نسخو کارولو موندلو لپاره د خلاصې سرچینې څارنې وسیلې (OSA تمرین) کاروي. ځینې ​​​​وختونه دا ممکنه نه ده چې دا ډول کتابتون خوندي نسخه ته تازه کړئ. په ځینو مواردو کې د فعالیت محدودیتونه شتون لري، په نورو کې هیڅ خوندي نسخه شتون نلري. په دې حالت کې، د SAST او OSA کړنالرو ترکیب به مرسته وکړي چې معلومه کړي چې هغه دندې چې د زیانمننې د استخراج لامل کیږي په کوډ کې نه کارول کیږي.

مګر ځینې وختونه ، په ځانګړي توګه کله چې جاواسکریپټ په پام کې ونیسئ ، دا ممکن په بشپړ ډول کوچني کار نه وي. لاندې یو حل دی، شاید مثالی نه وي، مګر بیا هم کار کوي، په برخه کې د زیان منونکو مثالونو په کارولو سره lodash په طریقو کې template и *set.

په JS فایل کې د احتمالي زیان منونکي کوډ ازموینې مثالونه:

/**
 * Template example
 */

'use strict';
var _ = require("./node_modules/lodash.js");


// Use the "interpolate" delimiter to create a compiled template.
var compiled = _.template('hello <%= js %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

// Use the internal `print` function in "evaluate" delimiters.

var compiled = _.template('<% print("hello " + js); %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

او کله چې مستقیم په html کې وصل کیږي:

<!DOCTYPE html>
<html>
<head>
    <title>Lodash Tutorial</title>
    <script src="./node_modules/lodash.js"></script>
    <script type="text/javascript">
  // Lodash chunking array
        nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];

        let c1 = _.template('<% print("hello " + js); %>!');
        console.log(c1);

        let c2 = _.template('<% print("hello " + js); %>!');
        console.log(c2);
    </script>
</head>
<body></body>
</html>

موږ د خپلو ټولو زیان منونکو میتودونو په لټه کې یو، کوم چې په زیان منونکو کې لیست شوي دي:

// Ищем все строки: в которых встречается строка lodash (предполагаем, что это объявление импорта библиотеки
CxList lodash_strings = Find_String_Literal().FindByShortName("*lodash*");

// Ищем все данные: которые взаимодействуют с этими строками
CxList data_on_lodash = All.InfluencedBy(lodash_strings);


// Задаем список уязвимых методов
List<string> vulnerable_methods = new List<string> {"template", "*set"};

// Ищем все наши уязвимые методы, которые перечисленны в уязвимостях и отфильтровываем их только там, где они вызывались
CxList vulnerableMethods = All.FindByShortNames(vulnerable_methods).FindByType(typeof(MethodInvokeExpr));

//Находим все данные: которые взаимодействуют с данными методами
CxList vulnFlow = All.InfluencedBy(vulnerableMethods);

// Если есть пересечение по этим данным - кладем в результат
result = vulnFlow * data_on_lodash;

// Формируем список путей по которым мы уже прошли, чтобы фильтровать в дальнейшем дубли
List<string> lodash_result_path = new List<string> {};

foreach(CxList lodash_result in result)
{
    // Очередной раз получаем пути к файлам
	string file_name = lodash_result.GetFirstGraph().LinePragma.FileName;
	lodash_result_path.Add(file_name);
}

// Дальше идет часть относящаяся к html файлам, так как в них мы не можем проследить откуда именно идет вызов
// Формируем массив путей файлов, чтобы быть уверенными, что срабатывания уязвимых методов были именно в тех файлах, в которых объявлен lodash
List<string> lodash_path = new List<string> {};
foreach(CxList string_lodash in lodash_strings)
{
	string file_name = string_lodash.GetFirstGraph().LinePragma.FileName;
	lodash_path.Add(file_name);
}

// Перебираем все уязвимые методы и убеждаемся, что они вызваны в тех же файлах, что и объявление/включение lodash
foreach(CxList method in vulnerableMethods)
{
	string file_name_method = method.GetFirstGraph().LinePragma.FileName;
	if (lodash_path.Contains(file_name_method) == true && lodash_result_path.Contains(file_name_method) == false){
		result.Add(method);
	}
}

// Убираем все UknownReferences и оставляем самый "длинный" из путей, если такие встречаются
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow) - result.FindByType(typeof(UnknownReference));

دنده: په غوښتنلیک کې شامل شوي سندونو لټون کول

د حل لاره: دا د غوښتنلیکونو لپاره غیر معمولي نه ده، په ځانګړې توګه د موبایل لپاره، د مختلفو سرورونو ته د لاسرسي یا د SSL-Pinning تصدیق کولو لپاره سندونه یا کیلي کارول. د امنیت له نظره، په کوډ کې د داسې شیانو ذخیره کول غوره عمل نه دی. راځئ هڅه وکړو چې یو قاعده ولیکئ چې په ذخیره کې ورته فایلونه وپلټئ:

// Найдем все сертификаты по маске файла
CxList find_certs = All.FindByShortNames(new List<string> {"*.der", "*.cer", "*.pem", "*.key"}, false);

// Проверим, где в приложении они используются
CxList data_used_certs = All.DataInfluencedBy(find_certs);

// И для мобильных приложений - можем поискать методы, где вызывается чтение сертификатов
// Для других платформ и приложений могут быть различные методы
CxList methods = All.FindByMemberAccess("*.getAssets");

// Пересечение множеств даст нам результат по использованию локальных сертификатов в приложении
result = methods * data_used_certs;

دنده: په غوښتنلیک کې د جوړ شوي ټکنونو موندل

د حل لاره: دا ډیری وختونه اړین دي چې جوړ شوي ټوکنونه یا نور مهم معلومات لغوه کړئ چې په کوډ کې شتون لري. البته، د سرچینې کوډ دننه د دوی ذخیره کول ښه نظر نه دی، مګر شرایط توپیر لري. د CxQL پوښتنو څخه مننه، د دې په څیر شیان موندل خورا اسانه دي:

// Получаем все строки, которые содержатся в коде
CxList strings = base.Find_Strings();

// Ищем среди всех строк нужное нам значение. В примере токен в виде строки "qwerty12345"
result = strings.FindByShortName("qwerty12345");

پایلې

زه امید لرم چې دا مقاله به د هغو کسانو لپاره ګټوره وي څوک چې د چیک مارکس وسیلې سره خپل پیژندنه پیل کوي. شاید هغه څوک چې د اوږدې مودې لپاره خپل قواعد لیکي په دې لارښود کې به یو څه ګټور ومومي.

له بده مرغه، اوس مهال د سرچینې نشتوالی شتون لري چیرې چې د چیک مارکس لپاره د قواعدو پراختیا په جریان کې نوي نظرونه راټول کیدی شي. له همدې امله موږ جوړ کړل په Github کې ذخیره، چیرې چې موږ به خپل کار پوسټ کړو ترڅو هرڅوک چې CxQL کاروي کولی شي پدې کې یو څه ګټور ومومي ، او همدارنګه فرصت ولري چې خپل کار له ټولنې سره شریک کړي. ذخیره د مینځپانګې ډکولو او جوړښت په پروسه کې ده ، نو مرسته کونکو ته ښه راغلاست ویل کیږي!

ستاسو د پاملرنې مننه!

سرچینه: www.habr.com

Add a comment