Si të shkruani rregulla për Checkmarx dhe të mos çmendeni

Përshëndetje, Habr!

Në punën e saj, kompania jonë shpesh merret me mjete të ndryshme për analizën statike të kodit (SAST). Nga kutia, të gjitha funksionojnë në mënyrë mesatare. Sigurisht, gjithçka varet nga projekti dhe teknologjitë e përdorura në të, si dhe sa mirë këto teknologji mbulohen nga rregullat e analizës. Në mendimin tim, një nga kriteret më të rëndësishme në zgjedhjen e një mjeti SAST është mundësia për ta përshtatur atë për karakteristikat e aplikacioneve tuaja, pra për të shkruar dhe ndryshuar rregullat e analizës ose, siç quhen shpesh, Kërkesat e Personalizuara.

Si të shkruani rregulla për Checkmarx dhe të mos çmendeni

Ne zakonisht 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ë shkruajtjen e rregullave të analizës për të.

Përmbajtja

Hyrje

Për fillim, do të donja të rekomandoj një nga artikujt e pakët në gjuhën ruse mbi veçoritë e shkruajtjes së kërkesave për Checkmarx. Ai u publikua në Habr në fund të vitit 2019 me titullin: «Përshendetje, Checkmarx!». Si të shkruani një kërkesë për Checkmarx SAST dhe të gjeni dobësi të mëdha..

Aty shqyrtohet hollësisht se si të shkruani kërkesat e para në gjuhën CxQL (Gjuha e Kërkesave Checkmarx) për një aplikacion testues dhe tregohet parimet kryesore të funksionimit të rregullave të analizës.

Nuk do të përsëris atë që është përshkruar atje, megjithatë disa përputhje do të jenë pranishme. Në artikullin tim do të përpiqem të krijoj një “mbledhje recetash”, një listë zgjidhjesh për probleme specifike me të cilat kam hasur gjatë punës sime me Checkmarx. Për shumë nga këto probleme, më ka dashur të bëj në një farë mënyre kërkime të thella. Disa herë ka munguar informacioni në dokumentacion dhe ndonjëherë ka qenë e vështirë të kuptohet se si të realizosh atë që kërkohej. Shpresoj se përvoja ime dhe netët pa gjumë nuk do të shkojnë kot, dhe kjo “mbledhje recetash për Kërkesat e Personalizuara” do t'ju kursejë disa orë ose disa qeliza nervore. Pra, le të fillojmë!

Informacion i përgjithshëm mbi rregullat

Fillimisht, le të shqyrtojmë disa koncepte themelore dhe procesin e punës me rregullat, për një kuptim më të mirë të asaj që do të ndodhë më vonë. Po ashtu, sepse në dokumentacion nuk thuhet për këtë ose është shumë e shpërndarë në strukturë, që nuk është shumë e përshtatshme.

  1. Rregullat aplikohen gjatë skanimit në varësi të preset-it të zgjedhur në fillim (grupi i rregullave aktive). Mund të krijoni një numër të pakufizuar preset-i dhe mënyra se si t’i strukturoni ato varet nga karakteristikat e procesit tuaj. Mund të grupohen sipas gjuhëve ose të dallohen presetet për çdo projekt. Numri i rregullave aktive ndikon në shpejtësinë dhe saktësinë e skanimit.

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniKonfigurimi i Preset në ndërfaqen e Checkmarx

  2. Rregullat redaktohen në një mjet të veçantë të quajtur CxAuditor. Ky është një aplikacion desktop që lidhet me serverin e Checkmarx. Ky mjet ka dy modulet e funksionimit: redaktimin e rregullave dhe analizimin e rezultateve të një skanimi të kryer tashmë.

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniNdërfaqja CxAudit

  3. Rregullat në Checkmarx ndahen sipas gjuhëve, pra për çdo gjuhë ekziston një grup i veçantë kërkesash. Po ashtu, ka disa rregulla të përbashkëta që aplikohen pavarësisht nga gjuha, këto quhen kërkesat themelore. Në shumicën e rasteve, kërkesat themelore përfshijnë kërkimin e informacionit që përdoret nga rregullat e tjera.

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniNdaje rregullat sipas gjuhëve

  4. Rregullat ndahen në “Executable” dhe “Non-Executable” (Të Ekzekutueshme dhe Jo Të Ekzekutueshme). Kjo emërtim nuk është krejtësisht i saktë, në mendimin tim, por ajo që është. Esenca ë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, janë thjesht funksione).

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniPërcaktimi i tipit të rregullit gjatë krijimit

  5. Mund të krijoni rregulla të reja ose të plotësoni/ri-shkruani ato ekzistuese. Për të ri-shkruar një rregull, duhet të gjeni atë në pemë, të klikoni me të djathtën dhe të zgjidhni opsionin “Override“ nga menuja që shfaqet. Këtu është e rëndësishme të mbani mend se rregullat e reja nuk janë fillimisht të përfshira në preset-e dhe nuk janë aktive. Për t'i filluar ato, duhet t'i aktivizoni në menunë “Preset Manager” në instrumentin. Rregullat e ri-shkruara mbajnë konfigurimet e tyre, pra, nëse një rregull ishte aktiv, do të mbetet ashtu dhe do të aplikohet menjëherë.

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniShembuj i një rregulli të ri në ndërfaqen e Preset Manager

  6. Gjatë ekzekutimit, ndihmohet në ndërtimin e një “pemë” kërkesash, ku çdo gjë varet nga diçka tjetër. Rregullat që marrin informacionin ekzekutohen së pari, ndërsa ato që e përdorin atë ekzekutohen së dyti. Rezultati i ekzekutimit ruhet në cache, kështu që nëse ka mundësi të përdoren rezultatet e një rregulli ekzistues, është më mirë ta bëni këtë, pasi do të reduktojë kohën e skanimit.

  7. 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 skanimet e projekteve në ekipin e zgjedhur.

  • Në nivelin e projektit — do të aplikohet në një projekt të caktuar.

    Si të shkruani rregulla për Checkmarx dhe të mos çmendeniPërcaktimi i nivelit ku 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 disa teknika që do ta thjeshtojnë ndjeshëm jetën.

Operacione me lista

- zbritja e njërit nga tjetri (list2 - list1)
* ndërfaqja e listave (list1 * list2)
+ shtimi i listave (list1 + list2)

& (logjika E) - bashkon listat në përputhje (list1 & list2), ngjashëm me ndërfaqen (list1 * list2)
| (logjika O) - bashkon listat në kërkimin e gjerë (list1 | list2)

Me listat nuk funksionon: ^ && || % / 

Të gjitha elementët e gjetura

Brenda gjuhës së skanuar, mund të marrësh listën e të gjitha elementeve që ka përcaktuar Checkmarx (string, funksione, klasa, metoda, etj.). Ky është një hapësirë objektesh, të cilës mund t’i qaseni nëpërmjet All. Kështu, për të kërkuar një objekt me emrin e caktuar searchMe, mund të bëni një kërkim, p.sh., për emrin në të gjithë objektet e gjetura:

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

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

Por, nëse duhet të bëni kërkimin në një gjuhë tjetër, e cila për ndonjë arsye nuk është përfshirë në skanimin (p.sh., groovy në një projekt për Android), mund të zgjerojmë hapësirën tonë të objekteve përmes variablës:

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

Funksione për analizën e Fluksit

Këto funksione përdoren në shumë rregulla dhe ja një pasqyra e vogël, çfarë tregojnë ato:

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

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

Marrja e emrit/rrugës së skedarit

Ka disa atribute që mund të merren nga rezultatet e ekzekutimit të kërkesës (emri i skedarit ku është gjetur shfaqja, stringu, etj.), por si t'i marrësh dhe t'i përdorësh ato në dokumentim nuk është e specifikuar. Kështu që, për ta bërë këtë, duhet të referohesh në pronën LinePragma dhe brenda saj do të gjeni objektet që na nevojiten:

// Для примера найдем все методы
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);

Duhet të kemi parasysh se FileName realiteti përmban rrugën e skedarit, pasi kemi përdorur metodën GetFirstGraph.

Rezultati i ekzekutimit

Brenda CxQL, ekziston një variabël speciale result, e cila kthen rezultatin e ekzekutimit të rregullit të shkruar nga ju. Ajo inicializohet menjëherë dhe mund të shkruani në të rezultatet ndërmjetëse, duke i ndryshuar dhe përcaktuar ato gjatë procesit të punës. Por, nëse brenda rregullit nuk ka caktim për këtë variabël ose funksion, kthehu— rezultati i ekzekutimit gjithmonë do të jetë zero.

Kërkesa e ardhshme nuk do të na kthejë asgjë si rezultat të ekzekutimit dhe gjithmonë do të mbetet e zbrazët:

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

Por, duke caktuar rezultatin e ekzekutimit në variablen magjike 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 ekvivalente të funksioneve në një gjuhë të zakonshme programuese. Gjatë shkrimit të një rregulli, ju mund të përdorni rezultatet e kërkesave të tjera. Si shembull, nuk është e nevojshme të kërkoni çdo herë të gjitha thirrjet e metodave në kod, mjafton të thirrni rregullin e duhur:

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

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

Ky qasje lejon të shkurtohet kodi dhe të zvogëlohet ndjeshëm koha e ekzekutimit të rregullit.

Zgjidhja e problemeve

Logimi

Kur punoni me mjetin, ndonjëherë nuk arrihet të shkruani menjëherë kërkesën e nevojshme dhe ndiheni të detyruar të eksperimenti, duke provuar variante të ndryshme. Për këtë rast, mjeti parashikon regjistrimin, i cili thirret si më poshtë:

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

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

Por duhet të mbani mend se ky metod pranon vetëm string, kështu që nuk do të jetë e mundur të shfaqni një listë të plotë të elementeve të gjetura si rezultat i operacionit të parë. Varianti i dytë, i cili përdoret për de bug-im — është herë pas here të caktoni variablës magjike result rezultati i ekzekutimit të kërkesës dhe të shihni se çfarë del. Ky qasje nuk është shumë e përshtatshme, duhet të jeni të sigurt se në kodin e mëpasshëm nuk ka ri-caktim ose operacione me këtë result ose thjesht të komenton kodin e vendosur më poshtë. Dhe mund të ndodhë siç më ndodhi, të harroj të heq disa nga këto thirrje në rregullin e gatshëm dhe të mendoj se pse nuk funksionon asgjë.

Një mënyrë më e përshtatshme — është të thirrni metodën kthehu me parametrin e nevojshëm. Në këtë rast, ekzekutimi i rregullit do të përfundojë dhe ne do të mund të shohim se çfarë ndodhi si rezultat i asaj që shkruam:

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

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

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

Problemi me identifikimin

Ka ndodhin situata kur nuk është e mundur të hyni në mjetin CxAudit (i cili përdoret për të shkruar rregulla). Ka shumë arsye për këtë, si mbyllja e papritur e programit, një përditësim papritmas i Windows-it, BSOD dhe situata të tjera të paparashikuara që janë jashtë kontrollit tonë. Në këtë rast, ndonjëherë mbetet një sesion i papërfunduar në bazën e të dhënave, i cili nuk lejon hyrjen e përsëritur. Për ta rregulluar këtë është e nevojshme të kryhen disa kërkesa:

Për Checkmarx para 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');

Shkruajtja e rregullave

Këtu arrijmë në pjesën më interesante. Kur filloni të shkruani rregulla në CxQL, shpesh ndiheni të pakët jo aq për dokumentacionin sa për disa shembuj konkretë të zgjidhjes së detyrave të caktuara dhe përshkrimit të procesit të punës me kërkesat në përgjithësi.

Do të përpiqem ta thjeshtoj pak jetën për ata që po fillojnë të zhytin në gjuhën e kërkesave dhe do të jap disa shembuj të përdorimit të Pyetjeve të Personalizuara për zgjidhjen e detyrave të caktuara. Disa prej tyre janë mjaft të përgjithshme dhe mund të aplikohen në kompaninë tuaj praktikisht pa ndryshime, ndërsa të tjera janë më specifike, por gjithashtu mund të përdoren duke ndryshuar kodin sipas specifikës së aplikacioneve tuaja.

Kështu, ja me cilat detyra na është dashur të përballemi më shpesh:

Detyra: Në rezultatet e ekzekutimit të rregullit ka disa Flow dhe një prej tyre është një nëngrup i tjetrit, e rëndësishme është të lini vetëm një prej tyre.

Zgjidhja: Vërtet, ndonjëherë Checkmarx tregon disa Flow të lëvizjeve të të dhënave që mund të ndërthuren dhe të jenë një version më të shkurtër të të tjerëve. Për këto raste ekziston një metodë speciale, ReduceFlow. Në varësi të parametrave, ajo do të zgjedhë Flow-n më të shkurtër ose më të gjatë:

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

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

Detyra: Zgjerojmë listën e të dhënave të ndjeshme, për të cilat reagojnë mjeti

Zgjidhja: Në Checkmarx ekzistojnë rregulla bazë, rezultatet e ekzekutimit të të cilave përdoren nga shumë kërkesa të tjera. Duke i shtuar disa prej këtyre rregullave me të dhëna të specifikuara për aplikacionin tuaj, mund të përmirësoni menjëherë rezultatet e skanimit. Më poshtë është një shembull rregulli, me të cilin mund të filloni:

General_privacy_violation_list

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: Zgjerojmë listën e variablave me fjalëkalim

Zgjidhja: Do të rekomandoja menjëherë të vëreni rregullin bazë për identifikimin e fjalëkalimeve në kod dhe t'i shtoni atij një listë emrash variablash që pranohet 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 default 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 formatet e përdorura, të cilat nuk mbështeten nga Checkmarx

Zgjidhja: Të gjitha kërkesat në Checkmarx janë të ndara sipas gjuhëve, kështu që është e nevojshme të plotësohen rregullat për çdo gjuhë. Më poshtë janë disa shembuj të tillë rregullash.

Nëse përdoren biblioteka që plotësojnë ose zëvendësojnë funksionalitetin standard — ato lehtë mund të shtohen në rregullin bazë. Kështu, të gjithë ata që e përdorin do të dinë menjëherë për hyrjet e reja. Si një shembull, bibliotekat për regjistrimin në Android — Timber dhe Loggi. Në paketën bazë të rregullave për identifikimin e thirrjeve jo sistemike nuk ka, kështu që nëse një fjalëkalim ose identifikues sesioni bie në log, nuk do ta mësojmë. Le të provojmë të shtojmë në rregullat e Checkmarx përkufizimin e metodave të tilla.

Shembulli testues i kodit, i cili përdor bibliotekën Timber për regjistrim:

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("Mesazhi i Gabimit");
        Timber.d("Mesazhi i Shkallës");

        Timber.tag("Një tag të Diferencuar").e("Dhe mesazhi i gabimit");
    }
}

Ja, këtu është një shembull kërkese për Checkmarx, që do të lejojë shtimin e përcaktimit të thirrjeve të metodave Timber, si një pikë dalëse të 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, mund të plotësojmë rregullin e afërt, por tani që i përket drejtpërdrejt logimit në Android:

FindAndroidLog_Outputs

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

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

Po ashtu, nëse aplikacionet Android përdorin WorkManager për punë asinkrone, është mirë të njoftojmë gjithashtu 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 variablave dhe vlerave të ndryshme në iOS përdoren skedarë të veçantë me shtrirje .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ë të dukshme me sy të 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 na njofton nëse ndonjëherë përmenden fjalëkalime apo tokene.

Shembulli i një skedari të tillë, në të cilin është i koduar një token për komunikim 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 gjatë shkrimit:

// Используем результат выполнения правила по поиску файлов 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ë dobishme për punën me XML dhe për kërkimin e vlerave, etiketave, atributeve dhe të tjerë. Por, fatkeqësisht, në dokumentacion ka një gabim, për shkak të së cilit asnjë shembull nuk funksionon. Megjithëse në versionin e fundit të dokumentacionit ky problem është rregulluar — kini kujdes, nëse përdorni versione më të hershme të dokumenteve.

Ja një shembull të gabuar nga dokumentacioni:

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

Si rezultat i përpjekjes për të ekzekutuar, ne do të marrim një gabim, se All nuk ka një metodë të tillë... Dhe kjo është e saktë, pasi për të përdorur funksionet për punën me XML ka një hapësirë të veçantë objekti — cxXPath. Kështu duket kërkesa e saktë për të kërkuar konfigurimin në Android, i cili lejon përdorimin e trafikut HTTP:

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

Le të shqyrtojmë më në hollësi, pasi sintaksa e të gjitha funksioneve është e ngjashme, pas të cilave, pasi të kuptoni një, vetëm duhet të zgjidhni atë që ju nevojitet. Pra, radhazi sipas parameterëve:

  • "*.xml"— maska e skedarëve, në të cilët duhet të bëhet kërkimi

  • 8 — id e gjuhës, për të cilën aplikohet rregulli

  • "cleartextTrafficPermitted"— emri i atributit në xml

  • "true" — vlera e këtij atributi

  • false — përdorimi i shprehjes së rregullt gjatë kërkimit

  • e vërtetë — do të thotë që kërkimi do të bëhet duke injoruar rastin, pra case-insensitive

Për shembull përdoret një rregull, i cili përcakton konfigurimet e pasakta, nga pikëpamja e sigurisë, të lidhjes në rrjet në Android, të cilat lejojnë komunikimin me serverin përmes protokollit HTTP. Një shembull konfigurimi, që përmban atributin cleartextTrafficPermitted me vlerë e vërtetë:

example.com
        
            
        
        
            secure.example.com

Detyra: Të kufizoni rezultatet sipas emrit/rrugës së skedarit

Zgjidhja: Në një nga projektet e mëdha, që lidhen me zhvillimin e një aplikacioni celular për Android, u përballëm me alarmet false të rregullit që përcakton konfigurimin e obfuscimit. Problemi është se rregulli nga kutia kërkon në skedar build.gradle konfigurimin që lidhet me zbatimin e rregullave të obfuscimit për versionin e lëshuar të aplikacionit.

Por në projekte të mëdha herë pas here hasen skedarë të nënkërkuar build.gradle, të cilat lidhen me bibliotekat e përfshira në projekt. Karakteristika është se, edhe nëse në këta skedarë nuk tregohet nevoja për obfuscim, gjatë kompilimit do të aplikohen konfigurimet e skedarit të prindit.

Pra, detyra qëndron në filtrimin e alarmeve në skedarët nënkërkuar, të cilat lidhen me bibliotekat. Mund të përcaktohen nga prania e vargut apply 'com.android.library'.

Shembulli i kodit nga skedari build.gradle, përcakton nevojën për obfuscim:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Shembulli i skedarit build.gradle për bibliotekën e përfshirë në projekt dhe pa një konfigurim të tillë:

apply 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);
		}
	}
}

Ky ky është një qasje mjaft universale dhe do të jetë e dobishme jo vetëm për aplikacionet Android, por edhe për raste të tjera kur duhet të përcaktohet ndaj cilit skedar i përket rezultati.

Detyra: Shto mbështetje për bibliotën e jashtme, nëse sintaksa nuk mbështetet plotësisht.

Zgjidhja: Numri i kornizave të ndryshme që përdoren gjatë procesit të kodimit është i jashtëzakonshëm. Sigurisht, Checkmarx nuk e di gjithmonë se ato ekzistojnë dhe detyra jonë është ta mësojmë të kuptojë që disa metoda i përkasin saktësisht këtij kornize. Ndonjëherë, kjo komplikohet nga fakti që kornizat përdorin emra funksionesh që janë shumë të zakonshëm dhe nuk është e mundur të përcaktojmë në mënyrë të qartë se cila thirrje i përket një biblioteke të caktuar.

Vështirësia qëndron në faktin se sintaksa e këtyre librarive nuk njihet gjithmonë siç duhet dhe na duhet të eksperimentojmë për të mos marrë një numër të madh të falsifikimeve. Ka disa mundësi për të përmirësuar saktësinë e skanimit dhe për të zgjidhur problemin e vendosur:

  • Mundësia e parë, ne e dimë saktësisht se biblioteka përdoret në një projekt të caktuar dhe mund të përdorim rregullin në nivelin e ekipit. Por në rast se ekipi vendos të përdorë një qasje tjetër ose përdor disa biblioteka ku kryqëzohen emrat e funksioneve, ne mund të marrim një pamje jo shumë të këndshme nga numri i madh i falsifikimeve.

  • Mundësia e dytë, të aplikojmë kërkimin në skedarët ku ndodhi qartë importi i bibliotekës. Me këtë qasje, ne mund të jemi të sigurt se në këtë skedar po përdoret biblioteka që na nevojitet.

  • Dhe mundësia e tretë është përdorimi i dy qasjeve të lartpërmendura gjithashtu bashkë.

Si një shembull, le të shqyrtojmë bibliotekën e njohur në rrethinat e caktuara. slick për gjuhën e programimit Scala, domethënë, funksionaliteti Splicing Literal Values. Në përgjithësi, për të transmetuar parametra në kërkesën SQL është e nevojshme të përdoret operatori $, i cili fut të dhënat në një kërkesë SQL të formuar paraprakisht. Pra, në fakt është një ekuivalent direkt i Prepared Statement në Java. Por, në rast nevoje për të ndërtuar dinamikisht kërkesën SQL, për shembull, nëse është e nevojshme të transmetoni emrat e tabelave, mund të përdoret operatori #$, i cili do të vendosë të dhënat drejtpërdrejt në kërkesë (praktikisht, si bashkimi i vargjeve).

Shembulli i kodit:

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

Checkmarx ende nuk di të identifikojë përdorimin e Splicing Literal Values dhe injoron operatorët #$, kështu që do të përpiqemi ta mësojmë atë të identifikojë potencialet SQL-injects dhe të nxjerrë në pah 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ërko funksionet e dobësuara të përdorura në bibliotekat Open-Source.

Zgjidhja: Në shumë kompani përdoren mjete për kontrollin e Open-Source (praktika OSA), që lejojnë të zbulohet përdorimi i versioneve të dobësuara të bibliotekave në aplikacionet e zhvilluara. Ndonjëherë nuk është e mundur të përditësohet një bibliotekë e tillë në një version të sigurt. Në disa raste ka kufizime funksionale, në të tjera nuk ka as një version të sigurt. Në këtë rast ndihmon kombinimi i praktikave SAST dhe OSA, që lejojnë të identifikohet se funksionet që çojnë në shfrytëzimin e dobësisë, nuk përdoren në kod.

Por ndonjëherë, sidomos nëse shqyrtojmë JavaScript, kjo mund të mos jetë një detyrë krejtësisht banale. Më poshtë është një zgjidhje, ndoshta jo ideale, por megjithatë funksionale, mbi shembujt e dobësive në komponentin lodash në metodat template dhe *set.

Shembuj të kodit potencialisht të dobësuar në skedarin 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 me lidhjen direkte në html:

<!DOCTYPE html>
<html>
<head>
    <title>Udhezuesi i 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ë metodet tona të dobësuara, të cilat janë të renditura 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ërko në aplikacionet për certifikatat e integruara.

Zgjidhja: Shpesh aplikacionet, sidomos ato mobile, përdorin certifikata ose çelësa për qasje në servera të ndryshëm ose për verifikimin e SSL-Pinning. Nëse e shohim nga perspektiva e sigurisë—të mbash këto gjëra 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ë repository:

// Найдем все сертификаты по маске файла
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ërkoni për token të komprometuar në aplikacion.

Zgjidhja: Shpesh është e nevojshme të tërhiqen token të komprometuar ose informacione të tjera të rëndësishme, që janë të pranishme në kod. Natyrisht, mbajtja e tyre brenda burimeve nuk është ide e mirë, por situatat ndodhin. Me ndihmën e kërkesave CxQL është mjaft e lehtë të gjejmë këto gjëra:

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

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

Përfundimi

Shpresoj se kjo artikull do të jetë e dobishme për ata që po fillojnë njohjen e tyre me mjetin Checkmarx. Ndoshta edhe ata që kanë kohë që shkruajnë rregullat e tyre do të gjejnë diçka të dobishme në këtë udhëzues.

Fatkeqësisht, tani po mungon një burim, ku mund të merret ide të reja gjatë zhvillimit të rregullave për Checkmarx. Prandaj e krijuam. repozitari në Github, ku do të publikojmë arritjet tona, në mënyrë që secili që përdor CxQL të mund të gjejë diçka të dobishme dhe të ketë mundësinë të ndajë punën e tij me komunitetin. Repository ë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

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster