Dumb Fuzzing

August 21, 2017

Test verisi oluşturmak çoğu yazılımcı için kazananı olmayan bir savaş. Ne kadar test case oluşturursanız oluşturun yeterli gelmiyor. Hele bir de iş uygulaması yazıyorsanız işler içinden çıkılmaz bir hale geliyor. Düşünelim ki bir araç kiralama firması için uygun araçları listeleyen bir modül yazıyorsunuz ve firmanın iş kuralları yeterince karışık. Örnek vermek gerekirse 2 yaşından büyük araçlara 3 günden fazla süren kiralamalar için müsaitlik verme gibi bir kıstasınız var. Bu sınırlamalarınızın sayısı çok fazla ise her durum için ayrı ayrı testini yazmak durumundasınız. Her durum için göre poco, pojo, dto ları uygun değişkenler ile doldurmak anlamına geliyor. Hali hazırda çalıştığım uygulamadan bir screenshot paylaşayım.

constructor

Görevi sadece test verisi ve işbirlikçilerini(collaborator) oluşturmak olan init fonksiyonu sadece 307 satırcık. Kabaca 300 satır diyebiliriz. Bu noktada yardımınıza fuzzing koşuyor.

Fuzzing yanlış, bozulmuş, eksik veya özel karakterler içeren streamler ile sistemi penetre etmeye yarayan bir teknik. Amacı ise uygulamayı hata vermeye zorlamak. Genel olarak güvenlik amacı ile kullanılsa testcase oluşturmak için de kullanılıyor.Hatanın yakalandığı yerde buffer overflow gibi bir hata alınması durumunda code/ command injection a kadar giden bir süreç. Yukarıda bahsettiğim araç kiralama uygulaması uygunluk modulünü aşağıdaki gibi implement edelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Vehicle
{
    public DateTime Year { get; set; }
    public int Age { get; set; }
    public Segment Segment { get; set; }
    public Transmisson Transmisson { get; set; }
    public PowerType PowerType { get; set; }
}

public class AvailabilityResponse
{
    public List<Vehicle> Vehicles { get; set; }
}

public class AvailabilityQuery
{
    public Segment Segment { get; set; }
    public Transmisson Transmisson { get; set; }
    public PowerType PowerType { get; set; }
    public DateTime Begin { get; set; }
    public DateTime End { get; set; }

    public override string ToString()
    {
        return $"Begin = {Begin}, End = {End}, PowerType = {PowerType}, Transmisson = {Transmisson}, Segment = {Segment} ";
    }
}

public interface IVehicleAvailabilityService
{
    AvailabilityResponse Check(AvailabilityQuery query);
}

public class VehicleAvailabilitySearchService : IVehicleAvailabilityService
{
    public AvailabilityResponse Check(AvailabilityQuery query)
    {
        // ...
        if (query.Begin.Date == DateTime.Today)
        {
            throw new InvalidOperationException("Special corner case or null pointer exception");
        }

        else if (query.Segment == Segment.NONE)
        {
            throw new ArgumentException("Segment is mandatory in case of very complex scenario happened");
        }
        // ...
        return new AvailabilityResponse();
    }
}

Gerçek işi yapan kısmı çıkarınca yukarıdaki gibi bir kod parçası elde ediyoruz. Ortaya çıkabilecek runtime exceptionları simule etmek için tuhaf exceptionlar atıyor çok takılmayın. Gelelim fuzzer implementasyonumuza. Yapılan işlem ilgili veri tipleri için tamamen random veri atamak. Başlıkta dikkat ettiğiniz gibi  tamamen aptal (Dumb) bir kutu.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public abstract class Fuzzer<T>
{
    protected Random rnd = new Random();
    public abstract T Create();
}

public class DateFuzzer : Fuzzer<DateTime>
{
    public override DateTime Create()
    {
        return DateTime.UtcNow.AddDays(rnd.Next(240));
    }
}

public class EnumFuzzer<T> : Fuzzer<T>
{
    public override T Create()
    {
        if (rnd.Next() % 1000 == 0)
            return default(T);

        T[] values = (T[])(Enum.GetValues(typeof(T)));
        return values[rnd.Next(1, values.Length)];
    }
}

public class AvailabilityQueryFuzzer : Fuzzer<AvailabilityQuery>
{
    private readonly EnumFuzzer<Segment> segmentFuzzer;
    private readonly EnumFuzzer<Transmisson> transmission;
    private readonly EnumFuzzer<PowerType> power;
    private readonly DateFuzzer dt;
    public AvailabilityQueryFuzzer()
    {
        segmentFuzzer = new EnumFuzzer<Segment>();
        transmission = new EnumFuzzer<Transmisson>();
        power = new EnumFuzzer<PowerType>();
        dt = new DateFuzzer();
    }

    public override AvailabilityQuery Create()
    {
        return new AvailabilityQuery()
        {
            Begin = dt.Create(),
            End = dt.Create(),
            PowerType = power.Create(),
            Segment = segmentFuzzer.Create(),
            Transmisson = transmission.Create()
        };
    }
}

Peki testimiz nasıl işliyor. Test altındaki servisimizin (SUT) exception atmasını beklemiyoruz. İnput olarak verilen bir query ye cevap olarak exception atması modulümüzde bir şeylerin doğru gitmediğini gösteriyor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Vehicle
{
    public DateTime Year { get; set; }
    public int Age { get; set; }
    public Segment Segment { get; set; }
    public Transmisson Transmisson { get; set; }
    public PowerType PowerType { get; set; }
}

public class AvailabilityResponse
{
    public List<Vehicle> Vehicles { get; set; }
}

public class AvailabilityQuery
{
    public Segment Segment { get; set; }
    public Transmisson Transmisson { get; set; }
    public PowerType PowerType { get; set; }
    public DateTime Begin { get; set; }
    public DateTime End { get; set; }

    public override string ToString()
    {
        return $"Begin = {Begin}, End = {End}, PowerType = {PowerType}, Transmisson = {Transmisson}, Segment = {Segment} ";
    }
}

public interface IVehicleAvailabilityService
{
    AvailabilityResponse Check(AvailabilityQuery query);
}

public class VehicleAvailabilitySearchService : IVehicleAvailabilityService
{
    public AvailabilityResponse Check(AvailabilityQuery query)
    {
        // ...
        if (query.Begin.Date == DateTime.Today)
        {
            throw new InvalidOperationException("Special corner case or null pointer exception");
        }

        else if (query.Segment == Segment.NONE)
        {
            throw new ArgumentException("Segment is mandatory in case of very complex scenario happened");
        }
        // ...
        return new AvailabilityResponse();
    }
}

Testi 1000 defa çalıştırdığım durumda aldığım hatalar aşağıdaki gibi.

Begin = 28.08.2017 14:14:03, End = 13.01.2018 14:14:03, PowerType = NONE, Transmisson =NONE, Segment = NONE> Segment is mandatory in case of very complex scenario happened

Begin = 18.08.2017 14:14:03, End = 20.03.2018 14:14:03, PowerType = GASOLINE, Transmisson = AUTOMATIC, Segment = B > Special corner case or null pointer exception

Bunun içine genetik ve arama algoritmaları girmesi ile smart fuzzing adını alıyor. Sisteme random veri vermek yerine potansiyel hata durumlarına daha yakın veri üretip tüm processi sizin için daha kolay hale getiriyor.

Artıları

Eksileri

Dahası için

hidden

The Review Is the Action Item

2024/05/30The Review Is the Action ItemI like to consider running an incident review to be its own action item. Other follow-ups emerging from it are a plus, but the point is to learn from incidents, and the review gives room for that to happen.This is no…

via Ferd.ca

Finding a New Software Developer Job

For the first time ever, I was laid off, and had to find a new software developer job. I managed to find a new one, but it took longer than I thought, and it was a lot of work. I … Continue reading →

via Henrik Warne's blog

HOWTO: Change your behavior

In theory, behavior change should be easy. At first glance, it seems like you control your behavior. So, if you desire different behavior, why doesn’t your behavior change as instantly as your desire to change it? In short, lasting change of habitual behavio…

via Matt Might's blog