Si të shkruani rregulla për Checkmarx pa u çmendur

Hej Habr!

Në punën tonë, kompania jonë shumë shpesh merret me mjete të ndryshme të analizës së kodit statik (SAST). Jashtë kutisë ata të gjithë punojnë mesatarisht. Sigurisht, gjithçka varet nga projekti dhe teknologjitë e përdorura në të, si dhe nga sa mirë këto teknologji mbulohen nga rregullat e analizës. Sipas mendimit tim, një nga kriteret më të rëndësishme kur zgjidhni një mjet SAST është aftësia për ta personalizuar atë në specifikat e aplikacioneve tuaja, domethënë, të shkruani dhe ndryshoni rregullat e analizës ose, siç quhen më shpesh, Pyetjet e personalizuara.

Si të shkruani rregulla për Checkmarx pa u çmendur

Ne përdorim më shpesh Checkmarx - një analizues kodi shumë interesant dhe i fuqishëm. Në këtë artikull do të flas për përvojën time të shkrimit të rregullave të analizës për të.

Përmbajtje

Hyrje

Për të filluar, unë do të doja të rekomandoja një nga artikujt e paktë në Rusisht në lidhje me veçoritë e shkrimit të pyetjeve për Checkmarx. Ajo u botua në Habré në fund të vitit 2019 me titullin: "Përshëndetje, Checkmarx!" Si të shkruani një pyetje Checkmarx SAST dhe të gjeni dobësi të lezetshme.

Ai shqyrton në detaje se si të shkruani pyetjet e para në CxQL (Checkmarx Query Language) për disa aplikacione testimi dhe tregon parimet bazë se si funksionojnë rregullat e analizës.

Nuk do të përsëris atë që përshkruhet në të, megjithëse disa kryqëzime do të jenë ende të pranishme. Në artikullin tim do të përpiqem të përpiloj një lloj "koleksioni recetash", një listë zgjidhjesh për problemet specifike që kam hasur gjatë punës sime me Checkmarx. M'u desh të bëja mendjen për shumë nga këto probleme. Ndonjëherë nuk kishte informacion të mjaftueshëm në dokumentacion, dhe ndonjëherë ishte e vështirë të kuptosh se si të bëhej ajo që kërkohej. Shpresoj që përvoja ime dhe netët pa gjumë të mos jenë të kota dhe ky "koleksion i recetave të pyetjeve të personalizuara" do t'ju kursejë disa orë ose disa qeliza nervore. Pra, le të fillojmë!

Informacion i përgjithshëm mbi rregullat

Së pari, le të shohim disa koncepte bazë dhe procesin e punës me rregullat, për të kuptuar më mirë se çfarë do të ndodhë më pas. Dhe gjithashtu sepse dokumentacioni nuk thotë asgjë për këtë ose është shumë i përhapur në strukturë, gjë që nuk është shumë e përshtatshme.

  1. Rregullat zbatohen gjatë skanimit në varësi të paracaktimit të zgjedhur në fillim (një grup rregullash aktive). Ju mund të krijoni një numër të pakufizuar paravendosjesh dhe saktësisht se si t'i strukturoni ato varet nga specifikat e procesit tuaj. Mund t'i gruponi sipas gjuhës ose të zgjidhni paracaktimet për secilin projekt. Numri i rregullave aktive ndikon në shpejtësinë dhe saktësinë e skanimit.

    Si të shkruani rregulla për Checkmarx pa u çmendurVendosja e paracaktimit në ndërfaqen Checkmarx

  2. Rregullat redaktohen në një mjet të veçantë të quajtur CxAuditor. Ky është një aplikacion desktopi që lidhet me një server që funksionon Checkmarx. Ky mjet ka dy mënyra funksionimi: redaktimi i rregullave dhe analizimi i rezultateve të një skanimi të kryer tashmë.

    Si të shkruani rregulla për Checkmarx pa u çmendurNdërfaqja CxAudit

  3. Rregullat në Checkmarx ndahen sipas gjuhës, domethënë secila gjuhë ka grupin e vet të pyetjeve. Ka edhe disa rregulla të përgjithshme që zbatohen pavarësisht gjuhës, këto janë të ashtuquajturat pyetje themelore. Në pjesën më të madhe, pyetjet bazë përfshijnë kërkimin e informacionit që përdorin rregullat e tjera.

    Si të shkruani rregulla për Checkmarx pa u çmendurNdarja e rregullave sipas gjuhës

  4. Rregullat janë "Eekzekutueshme" dhe "Jo-Ekzekutueshme" (Ekzekutuar dhe Nuk Ekzekutohet). Jo shumë emri i saktë, për mendimin tim, por kjo është ajo që është. Në fund të fundit është se rezultati i ekzekutimit të rregullave "E ekzekutueshme" do të shfaqet në rezultatet e skanimit në UI, dhe rregullat "Jo-ekzekutueshme" nevojiten vetëm për të përdorur rezultatet e tyre në kërkesa të tjera (në thelb, vetëm një funksion).

    Si të shkruani rregulla për Checkmarx pa u çmendurPërcaktimi i llojit të rregullit gjatë krijimit

  5. Mund të krijoni rregulla të reja ose të plotësoni/rishkruani ato ekzistuese. Për të rishkruar një rregull, duhet ta gjeni në pemë, klikoni me të djathtën dhe zgjidhni "Override" nga menyja rënëse. Është e rëndësishme të mbani mend këtu se rregullat e reja nuk janë përfshirë fillimisht në paracaktimet dhe nuk janë aktive. Për të filluar përdorimin e tyre, duhet t'i aktivizoni në menynë "Preset Manager" në instrument. Rregullat e rishkruara ruajnë cilësimet e tyre, domethënë nëse rregulli ishte aktiv, ai do të mbetet i tillë dhe do të zbatohet menjëherë.

    Si të shkruani rregulla për Checkmarx pa u çmendurShembull i një rregulli të ri në ndërfaqen e Menaxherit të Paracaktuar

  6. Gjatë ekzekutimit, ndërtohet një "pemë" kërkesash, e cila varet nga çfarë. Rregullat që mbledhin informacion ekzekutohen së pari dhe ata që e përdorin atë së dyti. Rezultati i ekzekutimit ruhet në memorie, kështu që nëse është e mundur të përdoren rezultatet e një rregulli ekzistues, atëherë është më mirë ta bëni këtë, kjo do të zvogëlojë kohën e skanimit.

  7. Rregullat mund të zbatohen në nivele të ndryshme:

  • Për të gjithë sistemin - do të përdoret për çdo skanim të çdo projekti

  • Në nivel ekipi (Ekipi) - do të përdoret vetëm për të skanuar projektet në ekipin e përzgjedhur.

  • Në nivel projekti - Do të aplikohet në një projekt specifik

    Si të shkruani rregulla për Checkmarx pa u çmendurPërcaktimi i nivelit në të cilin do të zbatohet rregulli

"Fjalori" për fillestarët

Dhe do të filloj me disa gjëra që më shkaktuan pyetje, dhe do të tregoj gjithashtu një sërë teknikash që do ta thjeshtojnë ndjeshëm jetën.

Operacionet me lista

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

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

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

Të gjithë artikujt e gjetur

Brenda gjuhës së skanuar, mund të merrni një listë të absolutisht të gjithë elementëve që Checkmarx ka identifikuar (vargjet, funksionet, klasat, metodat, etj.). Kjo është një hapësirë ​​e objekteve që mund të arrihet përmes All. Kjo do të thotë, për të kërkuar një objekt me një emër specifik searchMe, mund të kërkoni, për shembull, me emër në të gjitha objektet e gjetura:

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

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

Por, nëse keni nevojë të kërkoni në një gjuhë tjetër që për ndonjë arsye nuk është përfshirë në skanim (për shembull, groovy në një projekt Android), mund të zgjeroni hapësirën tonë të objektit përmes një ndryshoreje:

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

Funksionet për analizën e rrjedhës

Këto funksione përdoren në shumë rregulla dhe këtu është një fletë mashtrimi i vogël i kuptimit të tyre:

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

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

Marrja e emrit/shtegut të skedarit

Ka disa atribute që mund të merren nga rezultatet e një pyetësori (emri i skedarit në të cilin është gjetur hyrja, vargu, etj.), por dokumentacioni nuk thotë se si t'i merrni dhe përdorni ato. Pra, për ta bërë këtë, ju duhet të hyni në pronën LinePragma dhe objektet që na duhen do të vendosen brenda saj:

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

Vlen të kihet parasysh se FileName përmban në të vërtetë shtegun për në skedar, pasi kemi përdorur metodën GetFirstGraph.

Rezultati i ekzekutimit

Ekziston një variabël i veçantë brenda CxQL result, i cili kthen rezultatin e ekzekutimit të rregullit tuaj të shkruar. Ai inicializohet menjëherë dhe ju mund të shkruani rezultate të ndërmjetme në të, duke i ndryshuar dhe rafinuar ato ndërsa punoni. Por, nëse nuk ka caktim për këtë variabël ose funksion brenda rregullit return- rezultati i ekzekutimit do të jetë gjithmonë zero.

Kërkesa e mëposhtme nuk do të na kthejë asgjë si rezultat i ekzekutimit dhe do të jetë gjithmonë bosh:

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

Por, pasi të kemi caktuar rezultatin e ekzekutimit në rezultatin e ndryshores magjike, do të shohim se çfarë na kthen kjo thirrje:

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

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

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

Duke përdorur rezultatet e rregullave të tjera

Rregullat në Checkmarx mund të quhen analoge me funksionet në një gjuhë të rregullt programimi. Kur shkruani një rregull, mund të përdorni rezultatet e pyetjeve të tjera. Për shembull, nuk ka nevojë të kërkoni çdo herë për të gjitha thirrjet e metodës në kod, thjesht thirrni rregullin e dëshiruar:

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

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

Kjo qasje ju lejon të shkurtoni kodin dhe të reduktoni ndjeshëm kohën e ekzekutimit të rregullave.

Zgjidhja e problemeve

Prerjet

Kur punoni me mjetin, ndonjëherë nuk është e mundur të shkruani menjëherë pyetjen e dëshiruar dhe duhet të eksperimentoni, duke provuar opsione të ndryshme. Për një rast të tillë, mjeti ofron prerje, e cila quhet si më poshtë:

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

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

Por vlen të kujtohet se kjo metodë pranohet vetëm si hyrje varg, kështu që nuk do të jetë e mundur të shfaqet një listë e plotë e elementeve të gjetura si rezultat i operacionit të parë. Opsioni i dytë, i cili përdoret për korrigjimin e gabimeve, është që herë pas here t'i caktohet një ndryshoreje magjike result rezultatin e pyetjes dhe shikoni se çfarë ndodh. Kjo qasje nuk është shumë e përshtatshme; duhet të jeni të sigurt që nuk ka mbivendosje ose operacione me këtë në kodin pas result ose thjesht komentoni kodin më poshtë. Ose, si unë, mund të harroni të hiqni disa thirrje të tilla nga një rregull i gatshëm dhe të pyesni veten pse asgjë nuk funksionon.

Një mënyrë më e përshtatshme është thirrja e metodës return me parametrin e kërkuar. 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ë kemi shkruar:

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

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

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

Problemi me hyrjen

Ka situata kur nuk mund të përdorni mjetin CxAudit (i cili përdoret për të shkruar rregulla). Mund të ketë shumë arsye për këtë, duke përfshirë përplasjet, përditësimet e papritura të Windows, BSOD dhe situata të tjera të paparashikuara që janë jashtë kontrollit tonë. Në këtë rast, ndonjëherë ka një seancë të papërfunduar në bazën e të dhënave, e cila ju pengon të regjistroheni përsëri. Për ta rregulluar atë, duhet të kryeni disa pyetje:

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');

Rregullat e shkrimit

Tani kalojmë në pjesën më interesante. Kur filloni të shkruani rregulla në CxQL, ajo që shpesh ju mungon nuk është aq shumë dokumentacion sesa disa shembuj të gjallë të zgjidhjes së problemeve të caktuara dhe përshkrimi i procesit se si funksionojnë pyetjet në përgjithësi.

Do të përpiqem ta bëj jetën pak më të lehtë për ata që kanë filluar të zhyten në gjuhën e pyetjeve dhe të jap disa shembuj të përdorimit të pyetjeve të personalizuara për të zgjidhur probleme të caktuara. Disa prej tyre janë mjaft të përgjithshme dhe mund të përdoren në kompaninë tuaj praktikisht pa ndryshime, të tjerat janë më specifike, por mund të përdoren gjithashtu duke ndryshuar kodin për t'iu përshtatur specifikave të aplikacioneve tuaja.

Pra, këtu janë problemet që kemi hasur më shpesh:

Një detyrë: Ka disa Rrjedha në rezultatet e ekzekutimit të rregullit dhe njëri prej tyre është foleja e një tjetri, ju duhet të lini njërën prej tyre.

zgjidhje: Në të vërtetë, ndonjëherë Checkmarx tregon disa flukse të dhënash që mund të mbivendosen dhe të jenë një version i shkurtuar i të tjerëve. Ekziston një metodë e veçantë për raste të tilla ReduceFlow. Në varësi të parametrit, ai do të zgjedhë rrjedhën më të shkurtër ose më të gjatë:

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

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

Një detyrë: Zgjeroni listën e të dhënave të ndjeshme ndaj të cilave mjeti reagon

zgjidhje: Checkmarx ka rregulla bazë, rezultatet e të cilave përdoren nga shumë pyetje të tjera. Duke plotësuar disa nga këto rregulla me të dhëna specifike për aplikacionin tuaj, ju mund të përmirësoni menjëherë rezultatet e skanimit. Më poshtë është një rregull shembull për të filluar:

Lista_përgjithshme_shkelje e privatësisë

Le të shtojmë disa variabla që përdoren në aplikacionin tonë për të ruajtur informacione të ndjeshme:

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

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

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

Një detyrë: Zgjero listën e variablave me fjalëkalime

zgjidhje: Unë do të rekomandoja që menjëherë t'i kushtoni vëmendje rregullit bazë për përcaktimin e fjalëkalimeve në kod dhe t'i shtoni atij një listë të emrave të variablave që përdoren zakonisht në kompaninë tuaj.

Lista_shkelje_fjalëkalimi_privacy

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

Një detyrë: Shto korniza të përdorura që nuk mbështeten nga Checkmarx

zgjidhje: Të gjitha pyetjet në Checkmarx ndahen sipas gjuhës, kështu që ju duhet të shtoni rregulla për secilën gjuhë. Më poshtë janë disa shembuj të rregullave të tilla.

Nëse përdoren biblioteka që plotësojnë ose zëvendësojnë funksionalitetin standard, ato mund të shtohen lehtësisht në rregullin bazë. Atëherë të gjithë ata që e përdorin do të mësojnë menjëherë për prezantimet e reja. Si shembull, bibliotekat për regjistrimin në Android janë Timber dhe Loggi. Në paketën bazë, nuk ka rregulla për identifikimin e thirrjeve jo-sistem, kështu që nëse një fjalëkalim ose identifikues i sesionit futet në regjistër, ne nuk do të dimë për të. Le të përpiqemi të shtojmë përkufizime të metodave të tilla në rregullat e Checkmarx.

Shembull i kodit të testimit 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");
    }
}

Dhe këtu është një shembull i një kërkese për Checkmarx, i cili do t'ju lejojë të shtoni një përkufizim të thirrjes së metodave të Timber si një pikë daljeje për të dhënat nga aplikacioni:

GjeniAndroidOutputs

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

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

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

Dhe gjithashtu mund të shtoni në rregullin fqinj, por ky 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()
);

Gjithashtu, nëse përdorin aplikacionet Android Menaxher i Punës për punë asinkrone, është mirë të informoni gjithashtu Checkmarx për këtë duke shtuar një metodë për marrjen e të dhënave nga detyra getInputData:

FindAndroidRead

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

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

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

Një detyrë: Kërkimi i të dhënave të ndjeshme në plist për projektet iOS

zgjidhje: iOS shpesh përdor skedarë të veçantë me shtesën .plist për të ruajtur variabla dhe vlera të ndryshme. Ruajtja e fjalëkalimeve, shenjave, çelësave dhe të dhënave të tjera të ndjeshme në këta skedarë nuk rekomandohet, pasi ato mund të nxirren nga pajisja pa asnjë problem.

Skedarët Plist kanë veçori që nuk janë të dukshme me sy të lirë, por janë të rëndësishme për Checkmarx. Le të shkruajmë një rregull që do të kërkojë të dhënat që na duhen dhe do të na tregojë nëse fjalëkalimet ose tokenët janë përmendur diku.

Një shembull i një skedari të tillë, i cili përmban një shenjë për komunikim me shërbimin backend:

<?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>

Dhe një rregull për Checkmarx, i cili 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);

Një detyrë: Gjetja e informacionit në XML

zgjidhje: Checkmarx ka funksione shumë të përshtatshme për të punuar me XML dhe për të kërkuar vlera, etiketa, atribute dhe më shumë. Por, për fat të keq, pati një gabim në dokumentacion për shkak të të cilit nuk funksionon asnjë shembull i vetëm. Pavarësisht se ky defekt është eliminuar në versionin më të fundit të dokumentacionit, kini kujdes nëse përdorni versione të mëparshme të dokumenteve.

Këtu është një shembull i pasaktë nga dokumentacioni:

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

Si rezultat i përpjekjes së ekzekutimit, do të marrim një gabim që All nuk ka një metodë të tillë... Dhe kjo është e vërtetë, pasi ekziston një hapësirë ​​e veçantë, e veçantë objekti për përdorimin e funksioneve për të punuar me XML - cxXPath. Ja si duket pyetja e saktë për të gjetur një cilësim në Android që lejon përdorimin e trafikut HTTP:

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

Le ta shohim pak më në detaje, pasi sintaksa për të gjitha funksionet është e ngjashme, pasi të keni kuptuar një, atëherë thjesht duhet të zgjidhni atë që ju nevojitet. Pra, në mënyrë sekuenciale sipas parametrave:

  • "*.xml"— maskë e skedarëve që do të kërkohen

  • 8 — ID e gjuhës për të cilën zbatohet rregulli

  • "cleartextTrafficPermitted"— emri i atributit në xml

  • "true" - vlera e këtij atributi

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

  • true — do të thotë që kërkimi do të kryhet duke injoruar shkronjat e vogla, domethënë të pandjeshme ndaj rasteve

Si shembull, ne përdorëm një rregull që identifikon, nga pikëpamja e sigurisë, cilësimet e gabuara të lidhjes së rrjetit në Android që lejojnë komunikimin me serverin nëpërmjet protokollit HTTP. Shembull i një cilësimi që përmban një atribut cleartextTrafficPermitted me kuptim 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>

Një detyrë: Kufizoni rezultatet sipas emrit/shtegut të skedarit

zgjidhje: Në një nga projektet e mëdha që lidhet me zhvillimin e një aplikacioni celular për Android, kemi hasur në pozitive false të rregullit që përcakton cilësimin e errësimit. Fakti është se rregulli jashtë kutisë kërkon në skedar build.gradle një mjedis përgjegjës për zbatimin e rregullave të turbullimit për versionin e lëshimit të aplikacionit.

Por në projekte të mëdha ndonjëherë ka skedarë fëmijësh build.gradle, të cilat i referohen bibliotekave të përfshira në projekt. E veçanta është se edhe nëse këto skedarë nuk tregojnë nevojën për turbullim, cilësimet e skedarit të asamblesë prind do të aplikohen gjatë përpilimit.

Kështu, detyra është të ndërpriten shkaktarët në skedarët e fëmijëve që i përkasin bibliotekave. Ato mund të identifikohen nga prania e linjës apply 'com.android.library'.

Shembull i kodit nga skedari build.gradle, i cili përcakton nevojën për turbullim:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Shembull skedari build.gradle për një bibliotekë të përfshirë në projekt që nuk e ka këtë cilësim:

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

Kjo qasje mund të jetë mjaft universale dhe e dobishme jo vetëm për aplikacionet Android, por edhe për raste të tjera kur duhet të përcaktoni nëse një rezultat i përket një skedari specifik.

Një detyrë: Shto mbështetje për një bibliotekë të palës së tretë nëse sintaksa nuk mbështetet plotësisht

zgjidhje: Numri i kornizave të ndryshme që përdoren në procesin e shkrimit të kodit është thjesht jashtë grafikëve. Natyrisht, Checkmarx nuk di gjithmonë për ekzistencën e tyre, dhe detyra jonë është ta mësojmë atë të kuptojë se disa metoda i përkasin posaçërisht kësaj kornize. Ndonjëherë kjo është e ndërlikuar nga fakti se kornizat përdorin emra funksionesh që janë shumë të zakonshëm dhe është e pamundur të përcaktohet pa mëdyshje lidhja e një thirrjeje të veçantë me një bibliotekë specifike.

Vështirësia është se sintaksa e bibliotekave të tilla nuk njihet gjithmonë si duhet dhe ju duhet të eksperimentoni për të shmangur marrjen e një numri të madh pozitivësh të rremë. Ka disa opsione për të përmirësuar saktësinë e skanimit dhe për të zgjidhur problemin:

  • Opsioni i parë, ne e dimë me siguri se biblioteka përdoret në një projekt specifik dhe mund të zbatojë rregullin në nivel ekipi. Por nëse ekipi vendos të marrë një qasje të ndryshme ose përdor disa biblioteka në të cilat emrat e funksioneve mbivendosen, ne mund të marrim një pamje jo shumë të këndshme të shumë rezultateve false.

  • Opsioni i dytë është të kërkoni për skedarë në të cilët biblioteka është importuar qartë. Me këtë qasje, mund të jemi të sigurt se biblioteka që na nevojitet është përdorur saktësisht në këtë skedar.

  • Dhe opsioni i tretë është përdorimi i dy qasjeve të mësipërme së bashku.

Si shembull, le të shohim një bibliotekë të njohur në qarqe të ngushta i shkëlqyeshëm për gjuhën e programimit Scala, përkatësisht funksionalitetin Lidhja e vlerave letrare. Në përgjithësi, për të kaluar parametrat në një pyetje SQL, duhet të përdorni operatorin $, i cili zëvendëson të dhënat në një pyetje të paraformuar SQL. Kjo është, në fakt, është një analog i drejtpërdrejtë i Deklaratës së Përgatitur në Java. Por, nëse keni nevojë të ndërtoni në mënyrë dinamike një pyetje SQL, për shembull, nëse keni nevojë të kaloni emrat e tabelave, mund të përdorni operatorin #$, i cili do të zëvendësojë drejtpërdrejt të dhënat në pyetje (pothuajse si bashkimi i vargjeve).

Shembull i kodit:

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

Checkmarx nuk di ende se si të zbulojë përdorimin e Splicing Literal Values ​​dhe anashkalon operatorët #$, kështu që le të përpiqemi ta mësojmë atë të identifikojë injeksione të mundshme SQL dhe të nxjerrë në pah vendet e duhura 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));
}

Një detyrë: Kërkoni për funksione të cenueshme të përdorura në bibliotekat me burim të hapur

zgjidhje: Shumë kompani përdorin mjete monitorimi me burim të hapur (praktikë OSA) për të zbuluar përdorimin e versioneve të cenueshme 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 fare version të sigurt. Në këtë rast, një kombinim i praktikave SAST dhe OSA do të ndihmojë për të përcaktuar që funksionet që çojnë në shfrytëzimin e cenueshmërisë nuk përdoren në kod.

Por ndonjëherë, veçanërisht kur merret parasysh JavaScript, kjo mund të mos jetë një detyrë krejtësisht e parëndësishme. Më poshtë është një zgjidhje, ndoshta jo ideale, por megjithatë funksionon, duke përdorur shembullin e dobësive në komponent lodash në metoda template и *set.

Shembuj të testimit të kodit potencialisht të cenueshë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 lidheni drejtpërdrejt në 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>

Ne jemi duke kërkuar për të gjitha metodat tona të cenueshme, të cilat janë të renditura në dobësitë:

// Ищем все строки: в которых встречается строка 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));

Një detyrë: Kërkoni për certifikata të ngulitura në aplikacion

zgjidhje: Nuk është e pazakontë që aplikacionet, veçanërisht ato celulare, të përdorin certifikata ose çelësa për të hyrë në serverë të ndryshëm ose për të verifikuar SSL-Pinning. Nga pikëpamja e sigurisë, ruajtja e gjërave të tilla në kod nuk është praktika më e mirë. Le të përpiqemi të shkruajmë një rregull që do të kërkojë skedarë të ngjashëm 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;

Një detyrë: Gjetja e shenjave të komprometuara në aplikacion

zgjidhje: Shpesh është e nevojshme të revokohen shenjat e komprometuara ose informacione të tjera të rëndësishme që janë të pranishme në kod. Sigurisht, ruajtja e tyre brenda kodit burimor nuk është një ide e mirë, por situatat ndryshojnë. Falë pyetjeve CxQL, gjetja e gjërave të tilla është mjaft e lehtë:

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

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

Përfundim

Shpresoj që ky artikull të jetë i dobishëm për ata që po fillojnë njohjen e tyre me mjetin Checkmarx. Ndoshta ata që kanë shkruar rregullat e tyre për një kohë të gjatë do të gjejnë gjithashtu diçka të dobishme në këtë udhëzues.

Për fat të keq, aktualisht ka mungesë të një burimi ku mund të mblidheshin ide të reja gjatë zhvillimit të rregullave për Checkmarx. Kjo është arsyeja pse ne krijuam depo në Github, ku do të postojmë punën tonë në mënyrë që të gjithë ata që përdorin CxQL të gjejnë diçka të dobishme në të, dhe gjithashtu të kenë mundësinë të ndajnë punën e tyre me komunitetin. Depoja është në proces të plotësimit dhe strukturimit të përmbajtjes, kështu që kontribuuesit janë të mirëpritur!

Спасибо за внимание!

Burimi: www.habr.com

Shto një koment