Përshëndetje, Habr!
Në punën tonë, kompania jonë shpesh merret me mjete të ndryshme të analizës statike të kodit (SAST). Nga kuti, të gjitha ato funksionojnë mesatarisht. Sigurisht, gjithçka varet nga projekti dhe teknologjitë që përdoren në të, si dhe sa mirë këto teknologji mbulohen nga rregullat e analizës. Sipas mendimit tim, një nga kriteret më të rëndësishme në zgjedhjen e një mjeti SAST është mundësia për ta konfiguruar atë sipas karakteristikave të aplikacioneve tona, të shkruajmë dhe të modifikojmë rregullat e analizës ose, siç i quajnë më shpesh, Kërkesat e Personalizuara.

Ne më së shpeshti përdorim Checkmarx — një analizues kodi shumë interesant dhe të fuqishëm. Në këtë artikull do të flas për përvojën time në shkrimin e rregullave të analizës për të.
Përmbajtja
Hyrje
Për të filluar, do të doja të rekomandoja një nga artikujt e pakët në gjuhën ruse rreth veçorive të shkrimit të kërkesave për Checkmarx. Ai u publikua në Habr në fund të vitit 2019 me titullin: .
Ajo shqyrton në detaje se si të shkruani kërkesat e para në gjuhën CxQL (Gjuha e Kërkesës Checkmarx) për një aplikacion testimi dhe tregon parimet kryesore të punës së rregullave të analizës.
Nuk do të ripërkthej atë që është përshkruar aty, megjithatë disa përputhje do të jenë prani. Në artikullin tim do të përpiqem të përgatis një “të dhënat e receta” një listë zgjidhjesh të detyrave specifike me të cilat kam hasur gjatë punës sime me Checkmarx. Pjesërisht e kam menduar seriozisht për shumë nga këto detyra. Disa herë më ka munguar informacioni në dokumentacion, dhe herë të tjera ka qenë e vështirë të kuptohet se si të bëhet ajo që kërkohej. Shpresoj që përvoja ime dhe netët pa gjumë të mos shkojnë kot, dhe kjo “mbledhje e recetave të Kërkesave të Personalizuara” do t'ju kursejë disa orë ose disa qeliza nervore. Le të fillojmë!
Informacione të përgjithshme rreth rregullave
Fillimi, le të shqyrtojmë disa nocione bazë dhe procesin e punës me rregullat për një kuptim më të mirë të asaj që do të ndodhë më vonë. Gjithashtu, sepse në dokumentacionin për këtë nuk thuhet asgjë ose është përhapur shumë në strukturë, që nuk është shumë e përshtatshme.
Rregullat aplikohen gjatë skanimit në varësi të preset-it të zgjedhur në fillim (një grup rregullash aktive). Mund të krijoni një numër të pakufizuar preset-esh dhe se si t'i strukturosh ato varet nga veçoritë e procesit tuaj. Mund t'i gruposh ato sipas gjuhëve ose të veçosh preset për çdo projekt. Numri i rregullave aktive ndikon në shpejtësinë dhe saktësinë e skanimit.
Konfigurimi i Preset në ndërfaqen CheckmarxRregullat redaktohen në një mjet të veçantë të quajtur CxAuditor. Ky është një aplikacion desktop i cili lidhet me serverin e Checkmarx. Ky mjet ka dy mënyra funksionimi: redaktimi i rregullave dhe analiza e rezultateve të skanimit të kryer tashmë.
Ndërfaqja CxAuditRregullat në Checkmarx janë të ndara sipas gjuhëve, domethënë për çdo gjuhë ekziston një grup i veçantë kërkesash. Po ashtu, ka disa rregulla të përbashkëta që përdoren pavarësisht nga gjuha, këto janë rregullat bazë të quajtura. Në shumicën e rasteve, kërkesat bazë përmbajnë informacionin që përdorin rregulla të tjera.
Ndajësia e rregullave sipas gjuhëveRregullat janë “Executable” dhe “Non-Executable” (Ekzekutues dhe Jo Ekzekutues). Një emërtim që nuk është plotësisht korrekt, sipas mendimit tim, por kjo është. Thelbi është se rezultati i ekzekutimit të rregullave “Executable” do të shfaqet në rezultatet e skanimit në UI, ndërsa rregullat “Non-Executable” nevojiten vetëm për të përdorur rezultatet e tyre në kërkesa të tjera (në thelb — thjesht një funksion).
Definimi i tipit të rregullit gjatë krijimitMund të krijoni rregulla të reja ose të plotësoni/ri-shkruani ato ekzistuese. Për të rishkruar një rregull, duhet ta gjeni atë në pemë, të klikoni me të djathtën dhe në menunë e rënë të zgjidhni opsionin “Override“. Këtu është e rëndësishme të mbani mend se rregullat e reja nuk janë fillimisht të aktivizuara në preset dhe nuk janë aktive. Për t'i filluar ato, duhet t'i aktivizoni ato në menunë “Preset Manager” në mjet. Rregullat e ri-shkruara ruajnë cilësimet e tyre, domethënë, nëse një rregull ishte aktiv, do të mbetet aktiv dhe do të përdoret menjëherë.
Shembuj i një rregulli të ri në ndërfaqen e Menaxherit të PreseteveGjatë ekzekutimit ndërtohet një "pemë" kërkesash, çfarë është e varur nga çfarë. Rregullat që mbledhin informacionin ekzekutohen të parat, menjëherë pas tyre ato që e përdorin. Rezultatet e ekzekutimit ruhen, kështu që nëse ka mundësi për të përdorur rezultatet e një rregulli ekzistues, është më mirë të bëhet kështu, pasi do të reduktojë kohën e skanimit.
Rregullat mund të aplikohen në nivele të ndryshme:
Për tërë sistemin - do të përdoret për çdo skanim të çdo projekti.
Në nivelin e ekipit (Team) - do të aplikohet vetëm për skanimin e projekteve në ekipin e zgjedhur.
Në nivelin e projektit - do të aplikohet në një projekt specifik.
Përcaktimi i nivelit në të cilin do të aplikohet rregulli
“Fjalori” për fillestarët
Dhe do të filloj me disa gjëra që më kanë ngjallur pyetje, si dhe do të tregoj një sërë strategjish që do ta thjeshtëzojnë jetën.
Operacionet me lista
- zbritja e njërit nga tjetri (list2 - list1)
* ndërsa edhe kryqëzimi i listave (list1 * list2)
+ mbledhja e listave (list1 + list2)
& (logjika AND) - bashkon listat sipas përputhjes (list1 & list2), ngjashëm me kryqëzimin (list1 * list2)
| (logjika OR) - bashkon listat sipas kërkimit të gjerë (list1 | list2)
Nuk funksionon me listat: ^ && || % / Të gjitha elementet e gjetura
Brenda gjuhës së skanuar mund të merret një listë e të gjithë elementeve që ka përcaktuar Checkmarx (rreshtat, funksionet, klasat, metodat, etj.). Kjo është një hapësirë objektesh, të cilës mund t'i qaset përmes All. Pra, për të kërkuar një objekt me një emër specifik searchMe, mund të kryhet një kërkim, për shembull, sipas emrit për të gjitha objektet e gjetura:
// Такой запрос выдаст все элементы
result = All;
// Такой запрос выдаст все элементы, в имени которых присутствует “searchMe“
result = All.FindByName("searchMe");Por, nëse duhet të kryhet kërkimi në një gjuhë tjetër, e cila nga ndonjë arsye nuk u përfshi në skanim (për shembull, groovy në një projekt për Android), mund të zgjasim hapësirën tonë të objekteve përmes variablës:
result = AllMembers.All.FindByName("searchMe");Funksionet për analizën e Flow
Këto funksione përdoren në shumë rregulla dhe ja një shënim i vogël, se çfarë do të thonë ato:
// Какие данные second влияют на first.
// Другими словами - ТО (second) что влияет на МЕНЯ (first).
result = first.DataInfluencedBy(second);
// Какие данные first влияют на second.
// Другими словами - Я (first) влияю на ТО (second).
result = first.DataInfluencingOn(second);Korrja e emrit/rrugës së skedarit
Ekzistojnë disa atribute që mund të merret nga rezultatet e ekzekutimit të kërkesës (emri i skedarit në të cilin është gjetur shfaqja, rreshti, etj.), por si t'i marrim dhe t'i përdorim në dokumentacion nuk thuhet. Pra, për ta bërë këtë, duhet të referoheni te pronësia LinePragma dhe brenda saj do të gjenden objektet që na duhen:
// Для примера найдем все методы
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);Është e rëndësishme të kihet parasysh se FileName përmban në të vërtetë rrugën e skedarit, sepse ne përdorëm metodën GetFirstGraph.
Rezultati i ekzekutimit
Brenda CxQL është parashikuar një variabël speciale rezultati, e cila kthen rezultatin e ekzekutimit të rregullit tuaj të shkruar. Ajo inicializohet menjëherë dhe mund të shkruajmë në të rezultate të përkohshme, duke i ndryshuar dhe saktësuar ato në procesin e punës. Por, nëse brenda rregullit nuk ka caktim për këtë variabël ose funksion return— rezultati i ekzekutimit gjithmonë do të jetë zero.
Kërkesa e ardhshme nuk do na kthejë asgjë si rezultat dhe gjithmonë do të jetë bosh:
// Находим элементы foo
CxList libraries = All.FindByName("foo");Por, duke caktuar rezultatin e ekzekutimit në variablin magjik result — do të shohim se çfarë na kthen ky thirrje:
// Находим элементы foo
CxList libraries = All.FindByName("foo");
// Выводим, как результат выполнения правила
result = libraries
// Или еще короче
result = All.FindByName("foo");Përdorimi i rezultateve të ekzekutimit të rregullave të tjera
Rregullat në Checkmarx mund të quhen analoge të funksioneve në një gjuhë programimi të zakonshme. Gjatë shkruarjes së rregullit, ju mund të përdorni rezultatet e kërkesave të tjera. Për një shembull, nuk ka nevojë të kërkoni çdo herë të gjitha thirrjet e metodave në kod, mjafton të thërrisni rregullin e nevojshëm:
// Получаем результат выполнения другого правила
CxList methods = Find_Methods();
// Ищем внутри метод foo.
// Второй параметр false означает, что ищем без чувствительности к регистру
result = methods.FindByShortName("foo", false);Ky qasje lejon të shkurtosh kodin dhe të zvogëlosh ndjeshëm kohën e ekzekutimit të rregullit.
Zgjidhja e problemeve
Regjistrimi
Kur punoni me mjetin, ndonjëherë nuk arrini të shkruani menjëherë kërkesën e nevojshme dhe duhet të eksperimentoni, duke provuar variante të ndryshme. Për këtë rast, mjeti ka parashikuar një log, i cili thirret në këtë mënyrë:
// Находим что-то
CxList toLog = All.FindByShortName("log");
// Формируем строку и отправляем в лог
cxLog.WriteDebugMessage (“number of DOM elements =” + All.Count);Por është e rëndësishme të mbani mend se ky metod pranon vetëm një varg, kështu që nuk do të jetë e mundur të nxjerrësh një listë të plotë të elementeve të gjetura si rezultat i operacionit të parë. Varianti i dytë, që përdoret për debug — është përsëri caktimi i variablit magjik rezultati rezultati i ekzekutimit të kërkesës dhe të shohim se çfarë do të ndodhi. Ky qasje nuk është shumë e këndshme; duhet të jeni të sigurtë se në kod pas kësaj nuk ka ri-caktim ose operacione me këtë rezultati ose thjesht të komentoni kodin e vendosur më poshtë. Ose si unë, të harrosh të heqësh disa thirrje të tilla nga rregulli i gatshëm dhe të çuditesh pse nuk funksionon asgjë.
Një mënyrë më të përshtatshme — është të thërrisni metodën return me parametrin e nevojshëm. Në këtë rast, ekzekutimi i rregullit do të përfundojë dhe ne do të jemi në gjendje të shohim se çfarë arritëm si rezultat i asaj që shkruam:
// Находим что-то
CxList toLog = All.FindByShortName("log");
// Выводим результат выполнения
return toLog
//Все, что написано дальше не будет выполнено
result = All.DataInfluencedBy(toLog)Problemi me logimin
Ndonjëherë ndodhin situata kur nuk mund të hyni në mjetin CxAudit (i cili përdoret për të shkruar rregulla). Ka shumë arsye për këtë, përfshirë mbyllje të papritur, përditësime të menjëhershme të Windows, BSOD dhe situata të tjera të paparashikueshme, të cilat janë jashtë kontrollit tonë. Në këto raste, ndonjëherë mbetet një sesion i pa përfunduar në bazën e të dhënave, i cili nuk lejon hyrjen përsëri. Për ta rregulluar këtë, është e nevojshme të kryhen disa kërkesa:
Për Checkmarx deri në 8.6:
// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
Për Checkmarx pas 8.6:
// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM LoggedinUser WHERE (ClientType = 'Audit');
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE (ClientType = 'Audit');Shkrimi i rregullave
Këtu jemi duke arritur në pjesën më interesante. Kur filloni të shkruani rregulla në CxQL, shpesh nuk mungon aq shumë dokumentacioni, saqë mungojnë disa shembuj të qartë të zgjidhjes së problemeve dhe përshkrimi i procesit të punës së kërkesave në përgjithësi.
Do të përpiqem ta thjeshtoj pak jetën për ata që fillojnë të merren me gjuhën e kërkesave dhe do të jap disa shembuj të përdorimit të Kërkesave të Personalizuara për zgjidhjen e disa problemeve. Disa prej tyre janë mjaft të përgjithshme dhe mund të aplikohen në kompaninë tuaj pothuajse pa ndryshime, ndërsa të tjerët janë më specifikë, por gjithashtu mund të përdoren duke ndryshuar kodin për të përmbushur specifikat e aplikacioneve tuaja.
Pra, këtu janë disa probleme me të cilat jemi hasur më shpesh:
Detyra: Në rezultatet e ekzekutimit të rregullit, disa Flows dhe një prej tyre është një nënfluks i tjetrit, është e nevojshme të lëni vetëm një nga to.
Zgjidhja: Vërtet, ndonjëherë Checkmarx tregon disa Flows të lëvizjes së të dhënave, të cilat mund të korsheshin dhe të ishin një version i shkurtuar i të tjerëve. Për këto raste, ka një metodë të veçantë ReduceFlow. Në varësi të parametrave, ai do të zgjedhë Flown më të shkurtër ose më të gjatë:
// Оставить только длинные Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow);
// Оставить только короткие Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow);Detyra: Të zgjeroni listën e të dhënave të ndjeshme, për të cilat mjeti reagon
Zgjidhja: Në Checkmarx ekzistojnë rregulla bazë, rezultati i ekzekutimit të së cilave përdoret nga shumë kërkesa të tjera. Duke e plotësuar disa nga këto rregulla me të dhëna specifike për aplikacionin tuaj, mund të përmirësoni menjëherë rezultatet e skanimit. Më poshtë është një shembull i një rregulli me të cilin mund të filloni:
General_privacy_violation_list
Do të shtojmë disa variabla që përdoren në aplikacionin tonë për ruajtjen e informacionit të ndjeshëm:
// Получаем результат выполнения базового правила
result = base.General_privacy_violation_list();
// Ищем элементы, которые попадают под простые регулярные выражения. Можно дополнить характерными для вас паттернами.
CxList personalList = All.FindByShortNames(new List<string> {
"*securityToken*", "*sessionId*"}, false);
// Добавляем к конечному результату
result.Add(personalList);Detyra: Të zgjeroni listën e variablave me fjalëkalime
Zgjidhja: Unë do të rekomandoja që të ktheni vëmendjen në rregullin themelor për përcaktimin e fjalëkalimeve në kod dhe të shtoni një listë emrash variablash që janë të pranuar të përdoren në kompaninë tuaj.
Password_privacy_violation_list
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*"));
// Shtojmë listën e defoltit të variablave
List pswdIncludeList = new List{"*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 pswdExcludeList = new List{"*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);
}
}Detyra: Shtoni frakset e përdorura që nuk mbështeten nga Checkmarx
Zgjidhja: Të gjitha kërkesat në Checkmarx janë të ndara sipas gjuhëve, kështu që rregullat duhet të plotësohen për secilën gjuhë. Më poshtë janë disa shembuj të tillë rregullash.
Nëse përdoren biblioteka që plotësojnë ose zëvendësojnë funksionalitetin standard — ato janë të lehta për t'u shtuar në rregullin themelor. Atëherë të gjithë ata që e përdorin atë — do të dinë menjëherë për këto shqetësime të reja. Si shembull, bibliotekat për regjistrimin në Android — Timber dhe Loggi. Në paketën e bazës të rregullave për përcaktimin e thirrjeve jo sistematike nuk ka, kështu që nëse fjalëkalimi ose identifikuesi i sesionit përfshihet në regjistrim, ne nuk do të njoftohemi për këtë. Do të përpiqemi të shtojmë në rregullat Checkmarx përcaktimin e këtyre metodave.
Shembulli testues i kodit që përdor bibliotekën Timber për regjistrimin:
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");
}
}Këtu është një shembull i kërkesës për Checkmarx, i cili do të mundësojë shtimin e përcaktimit të thirrjeve të metodave Timber, si një pikë dalëse e të dhënave nga aplikacioni:
FindAndroidOutputs
// Получаем результат выполнения базового правила
result = base.Find_Android_Outputs();
// Дополняем вызовами, которые приходят из библиотеки Timber
CxList timber = All.FindByExactMemberAccess("Timber.*") +
All.FindByShortName("Timber").GetMembersOfTarget();
// Добавляем к конечному результату
result.Add(timber);Gjithashtu, mundet të plotësohet rregulli fqinj, por tashmë që lidhet drejtpërdrejt me regjistrimin në Android:
FindAndroidLog_Outputs
// Получаем результат выполнения базового правила
result = base.Find_Android_Log_Outputs();
// Дополняем вызовами, которые приходят из библиотеки Timber
result.Add(
All.FindByExactMemberAccess("Timber.*") +
All.FindByShortName("Timber").GetMembersOfTarget()
);Po ashtu, nëse në aplikacionet Android përdoret për punë të asinkronizuar, do të ishte mirë të njoftohej edhe Checkmarx duke shtuar metodën e marrjes së të dhënave nga detyra getInputData:
FindAndroidRead
// Получаем результат выполнения базового правила
result = base.Find_Android_Read();
// Дополняем вызовом функции getInputData, которая используется в WorkManager
CxList getInputData = All.FindByShortName("getInputData");
// Добавляем к конечному результату
result.Add(getInputData.GetMembersOfTarget());Detyra: Kërkimi i të dhënave të ndjeshme në plist për projektet iOS
Zgjidhja: shpesh për ruajtjen e ndryshoreve dhe vlerave të ndryshme në iOS përdoren skedarë të veçantë me zgjatjen .plist. Ruajtja e fjalëkalimeve, tokeneve, çelësave dhe të dhënave të tjera të ndjeshme në këta skedarë nuk rekomandohet, pasi ato mund të nxirren lehtësisht nga pajisja.
Skedarët plist kanë veçori që nuk janë evidente për syrin e zakonshëm, por janë të rëndësishme për Checkmarx. Do të shkruajmë një rregull që do të kërkojë të dhënat që na nevojiten dhe do të na njoftojë nëse fjalëkalimet ose tokenerat përmenden diku.
Një shembull i një skedari të tillë, në të cilin është e koduar një token për komunikimin me shërbimin backend:
DeviceDictionary
phone
iPhone 6s
privatekey
MIICXAIBAAKBgQCqGKukO1De7zhZj6+Dhe një rregull për Checkmarx, në të cilin ka disa nuanca që duhet të merren parasysh kur shkruani:
// Используем результат выполнения правила по поиску файлов 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);Detyra: Kërkimi i informacionit në XML
Zgjidhja: Në Checkmarx ka funksione shumë të përshtatshme për punë me XML dhe kërkimin e vlerave, etiketimeve, atributeve dhe më shumë. Por në dokumentacionin, fatkeqësisht, është bërë një gabim për shkak të së cilës asnjë shembull nuk funksionon. Megjithëse në versionin e fundit të dokumentacionit ky defekt është eliminuar — bëni kujdes nëse përdorni versione më të hershme të dokumenteve.
Ja një shembull i gabuar nga dokumentacioni:
// Код работать не будет
result = All.FindXmlAttributesByNameAndValue("*.app", 8, “id”, "error- section", false, true);Si rezultat i përpjekjes së ekzekutimit, ne do të marrim një gabim që na All nuk ka këtë metodë… Dhe kjo është e saktë, pasi për të përdorur funksionet për punë me XML ka një hapësirë të veçantë objektesh — cxXPath. Ja si duket kërkesa e duhur për të gjetur cilësimin në Android që lejon përdorimin e trafikut HTTP:
// Правильный вариант с использованием cxXPath
result = cxXPath.FindXmlAttributesByNameAndValue("*.xml", 8, "cleartextTrafficPermitted", "true", false, true);Le të shqyrtojmë pak më tepër, pasi sintaksa e të gjithë funksioneve është e ngjashme; pasi të keni kuptuar një, duhet vetëm të zgjidhni atë që ju nevojitet. Pra, me radhë për parametrat:
"*.xml"— maska e skedarëve, mbi të cilën duhet të kryhet kërkimi8— id e gjuhës për të cilën zbatohet rregulli"cleartextTrafficPermitted"— emri i atributit në xml"true"— vlera e këtij atributifalse— përdorimi i shprehjeve të rregullta gjatë kërkimittrue— do të thotë se kërkimi do të kryhet duke injoruar rastin e shkronjave, dmth pa marrë parasysh madhësinë e shkronjave
Për shembull, është përdorur një rregull që përcakton cilësime të pasakta, nga pikëpamja e sigurisë, për lidhjen rrjetore në Android, të cilat lejojnë komunikimin me serverin përmes protokollit HTTP. Një shembull cilësimi që përmban atributin cleartextTrafficPermitted me vlerën true:
example.com
secure.example.comDetyra: Kufizoni rezultatet sipas emrit/rrugës së skedarit
Zgjidhja: Në një nga projektet e mëdha lidhur me zhvillimin e një aplikacioni mobil për Android, u përballëm me alarme false për rregullin që përcakton cilësimin e obfuskimit. Kjo për shkak se rregulli nga fabrika kërkon në dosje build.gradle cilësimin që përgjigjet për zbatimin e rregullave të obfuskimit për versionin e lëshimit të aplikacionit.
Por në projekte të mëdha herë pas here hasen skedarë dytësorë build.gradle, të cilat i përkasin bibliotekave të përfshira në projekt. Karakteristika është se, edhe nëse në këto skedarë nuk është e përmendur nevoja për obfuskimin, gjatë kompilimit do të zbatohen cilësimet e skedarit prind.
Pra, detyra qëndron në ndalimin e alarmeve në skedarët dytësor që ndërlidhen me bibliotekat. Mund të përcaktohen nga prania e vargut apply 'com.android.library'.
Shembuj kodi nga dosja build.gradle, që përcakton nevojën për obfuskimin:
apliko plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
...
}
buildTypes {
release {
minifyEnabled true
...
}
}
}
dependencies {
...
}Shembuj i skedarit build.gradle për bibliotekën e përfshirë në projekt dhe që nuk ka një konfigurim të tillë:
apliko plugin: 'android-library'
dependencies {
compile 'com.android.support:support-v4:18.0.+'
}
android {
compileSdkVersion 14
buildToolsVersion '17.0.0'
...
}Dhe rregulli për Checkmarx:
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);
}
}
}Një qasje e tillë mund të jetë mjaft universale dhe mund të përdoret jo vetëm për aplikacione Android, por edhe për raste të tjera kur nevojitet të përcaktojmë përkatësinë e rezultatit ndaj një skedari të caktuar.
Detyra: Shtoni mbështetje për një bibliotekë të tretë, nëse sintaksisi nuk mbështetet plotësisht
Zgjidhja: Numri i çfarëdo kornizash që përdoren gjatë procesit të shkrimit të kodit është thjesht i papërmbajtshëm. Sigurisht, Checkmarx nuk e di gjithmonë për ekzistencën e tyre dhe detyra jonë është ta mësojmë atë të kuptojë se disa metoda i përkasin pikërisht kësaj kornize. Ndërsa ndonjëherë kjo përkeqësohet nga fakti se kornizat përdorin emra funksionesh që janë shumë të zakonshme dhe nuk mund të përcaktohet qartë lidhja e ndonjë thirrjeje me një bibliotekë specifike.
Vështirësia qëndron në faktin se sintaksisi i këtyre biblioteka nuk njihet gjithmonë saktësisht dhe na duhet të eksperimentojmë për të mos marrë një numër të madh alarmesh false. Ka disa mundësi për të përmirësuar saktësinë e skanimit dhe për të zgjidhur problemin e paraqitur:
Mundësia e parë, ne e dimë saktësisht se biblioteka përdoret në një projekt të caktuar dhe mund të aplikojmë rregullin në nivel ekipi. Por në rast se ekipi vendos të përdorë një qasje tjetër ose përdor disa biblioteka, të cilat hasin emra funksionesh, mund të marrim një pamje të pakëndshme nga alarmin e shumtë false.
Mundësia e dytë, të aplikojmë një kërkim nëpër skedarë, në të cilët ndodhet qartë importi i bibliotekës. Në këtë qasje, ne do të jemi të sigurt se në këtë skedare përdoret saktësisht biblioteka që na nevojitet.
Dhe mundësia e tretë, është përdorimi i të dyja qasjeve të lartpërmendura së bashku.
Si një shembull, le të shqyrtojmë një bibliotekë të njohur në qarqet e ngushta për gjuhën e programimit Scala, dhe konkretisht, funksionalitetin Në përgjithësi, për të transmetuar parametra në një SQL kërkesë, është e nevojshme të përdoret operatori $, i cili vendos të dhënat në një SQL kërkesë të formuar paraprakisht. Kështu, në fakt, është një analog i drejtpërdrejtë i Prepared Statement në Java. Por, në rast se është e nevojshme të ndërtohet dinamik një SQL kërkesë, për shembull, nëse duhet të transmetohen emrat e tabelave, mund të përdoret operatori #$, i cili do t'i vendosë direkt të dhënat në kërkesë (gati si konkatenimi i stringjeve).
Shembulli i kodit:
// В общем случае - значения, контролируемые пользователем
val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOptionCheckmarx ende nuk di të përcaktojë përdorimin e Splicing Literal Values dhe injoron operatorët #$, kështu që le të përpiqemi ta mësojmë të identifikojë infeksionet potenciale SQL dhe të shfaqë vendet e nevojshme në kod:
// Находим все импорты
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));
}Detyra: Kërkimi i funksioneve të përdorura të prekshme në bibliotekat Open-Source
Zgjidhja: Në shumë kompani përdoren mjete për të kontrolluar Open-Source (praktika OSA), të cilat lejojnë zbulimin e përdorimit të versioneve të prekshme të bibliotekave në aplikacionet që po zhvillohen. Ndonjëherë, për të përditësuar një bibliotekë të tillë në një version të sigurt nuk është e mundur. Në disa raste ka kufizime funksionale, në të tjera nuk ka një version të sigurt. Në një rast të tillë, një kombinim i praktikave SAST dhe OSA do të ndihmojë për të përcaktuar se funksionet që çojnë në shfrytëzimin e një dobësie nuk janë përdorur në kod.
Por ndonjëherë, veçanërisht kur shqyrtojmë JavaScript, kjo mund të jetë një detyrë jo krejtësisht triviale. Më poshtë paraqitet një zgjidhje, ndoshta jo ideale, por megjithatë funksionale, me shembuj të dobësive në komponentin lodash në metodat template dhe *set.
Shembuj të kodit potencialisht të prekshëm në një skedar 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!'Dhe kur lidhet drejtpërdrejt në html:
<!DOCTYPE html>
<html>
<head>
<title>Tutoriali Lodash</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>Kërkojmë të gjithë metodat tona të prekshme, të cilat janë të listuara në dobësi:
// Ищем все строки: в которых встречается строка 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));Detyra: Kërkimi i certifikatave të ngulitura në aplikacion
Zgjidhja: Shpesh aplikacionet, veçanërisht ato mobile, përdorin certifikata ose çelësa për të aksesuar servera të ndryshëm ose për të verifikuar SSL-Pinning. Nëse e shohim nga këndvështrimi i sigurisë — mbajtja e këtyre gjërave në kod nuk është praktika më e mirë. Le të përpiqemi të shkruajmë një rregull që do të kërkojë skedarë të tillë në depo:
// Найдем все сертификаты по маске файла
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;Detyra: Kërkimi i tokeneve të kompromentuara në aplikacion
Zgjidhja: Ndonjëherë është e nevojshme të revokohen tokenat e komprometuar ose informacione të tjera të rëndësishme që janë të pranishme në kod. Sigurisht, ruajtja e tyre brenda burimeve nuk është ideja më e mirë, por situata ndryshon. Falë pyetjeve CxQL, gjetja e gjërave të tilla është mjaft e thjeshtë.
// Получаем все строки, которые содержатся в коде
CxList strings = base.Find_Strings();
// Ищем среди всех строк нужное нам значение. В примере токен в виде строки "qwerty12345"
result = strings.FindByShortName("qwerty12345");Përfundim
Shpresoj që ky artikull do t'i ndihmojë ata që po fillojnë të njihen me mjetin Checkmarx. Ndoshta edhe ata që kanë shkruar rregullat e tyre prej kohësh do të gjejnë diçka të dobishme në këtë udhëzues.
Fatkeqësisht, tani ka një mungesë të burimeve ku mund të ndihmohesh për të gjetur ide të reja gjatë zhvillimit të rregullave për Checkmarx. Prandaj kemi krijuar , ku do të publikojmë arritjet tona, në mënyrë që çdo kush që përdor CxQL të mund të gjejë diçka të dobishme dhe gjithashtu të ketë mundësinë të ndajë punimet e tij me komunitetin. Repozitori është në procesin e mbushjes dhe strukturimit të përmbajtjes, kështu që kontribuesit janë të mirëpritur!
Faleminderit për vëmendjen!
Burimi: habr.com

Konfigurimi i Preset në ndërfaqen Checkmarx
Ndërfaqja CxAudit
Ndajësia e rregullave sipas gjuhëve
Definimi i tipit të rregullit gjatë krijimit
Shembuj i një rregulli të ri në ndërfaqen e Menaxherit të Preseteve
Përcaktimi i nivelit në të cilin do të aplikohet rregulli