覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

MavenでOWASP Dependency CheckによるJavaライブラリの脆弱性をチェックする

OWASP Dependency Checkで使用しているJavaライブラリの脆弱性をチェックすることができます。

今回はMavenのpluginを使用します。


設定

pom.xmlに以下を追記します。

<plugins>
・・・
  <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>2.0.0</version>
    <configuration>
        <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
  </plugin>
・・・
</plugins>


脆弱性のチェック

pom.xmlへの追記が終わった状態で以下コマンドをを実行します

mvn dependency-check:check


すると以下のような出力がされます

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building sample-app 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- dependency-check-maven:2.0.0:check (default-cli) @ srcl ---
[INFO] Checking for updates
[INFO] starting getUpdatesNeeded() ...
[INFO] Download Started for NVD CVE - Modified
[INFO] Download Complete for NVD CVE - Modified  (5176 ms)
[INFO] Processing Started for NVD CVE - Modified
[INFO] Processing Complete for NVD CVE - Modified  (6004 ms)
[INFO] Begin database maintenance.
[INFO] End database maintenance.
[INFO] Check for updates complete (14381 ms)
[INFO] Analysis Started
[INFO] Finished Archive Analyzer (1 seconds)
[INFO] Finished File Name Analyzer (0 seconds)
[INFO] Finished Jar Analyzer (0 seconds)
[INFO] Finished Central Analyzer (4 seconds)
[INFO] Finished Dependency Merging Analyzer (0 seconds)
[INFO] Finished Version Filter Analyzer (0 seconds)
[INFO] Finished Hint Analyzer (0 seconds)
[INFO] Created CPE Index (1 seconds)
[INFO] Finished CPE Analyzer (1 seconds)
[INFO] Finished False Positive Analyzer (0 seconds)
[INFO] Finished Cpe Suppression Analyzer (0 seconds)
[INFO] Finished NVD CVE Analyzer (0 seconds)
[INFO] Finished Vulnerability Suppression Analyzer (0 seconds)
[INFO] Finished Dependency Bundling Analyzer (0 seconds)
[INFO] Analysis Complete (8 seconds)
[WARNING]

One or more dependencies were identified with known vulnerabilities in sample-app:

commons-beanutils-1.8.3.jar (commons-beanutils:commons-beanutils:1.8.3, cpe:/a:apache:commons_beanutils:1.8.3) : CVE-2014-0114


See the dependency-check report for more details.


[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.974 s
[INFO] Finished at: 2017-07-06T18:33:09+09:00
[INFO] Final Memory: 25M/899M
[INFO] ------------------------------------------------------------------------

今回の結果ではcommons-beanutilsの1.8.3 にCVE-2014-0114の脆弱性があることが指摘されています。


というわけで、簡単にJavaライブラリの脆弱性チェックができました。



関連エントリ

RxJava - RxJavaでHelloWorld

RxJavaを使いながらリアクティブプログラミングを理解していきたい。

とりあえずは、まず何を置いてもHello World!


準備

pom.xmlに以下の依存関係を追加します。Reactive Streamsにも対応しているバージョン2.Xを使います

<dependency>
    <groupId>io.reactivex.rxjava2</groupId>
    <artifactId>rxjava</artifactId>
    <version>2.0.8</version>
</dependency>


サンプルの実行

■サンプルコード

以下は、ほぼ「RxJavaリアクティブプログラミング (CodeZine BOOKS) 」からの写経です。

Hello, World!-Xの文字列を順に発行して、購読側のonNextで受け取れることを確認しました

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.schedulers.Schedulers;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.util.Arrays;
import java.util.List;

public class HelloWorldRx {

    public static void main(String[] args) throws Exception {
        // 発行するデータ
        final List<String> helloList = Arrays.asList("Hello, World!-1", "Hello, World!-2", "Hello, World!-3");

        Flowable<String> flowable = Flowable.create(new FlowableOnSubscribe<String>() {

            @Override
            public void subscribe(FlowableEmitter<String> emitter) throws Exception {
                helloList.forEach(s -> emitter.onNext(s));

                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER);


        flowable.observeOn(Schedulers.computation())
            .subscribe(new Subscriber<String>() {

                private Subscription subscription;

                @Override
                public void onSubscribe(Subscription subscription) {
                    this.subscription = subscription;
                    this.subscription.request(1);
                }

                @Override
                public void onNext(String data) {
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + " onNext -> " + data);

                    this.subscription.request(1);
                }

                @Override
                public void onComplete() {
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + " onComplete");
                }

                @Override
                public void onError(Throwable throwable) {
                    throwable.printStackTrace();
                }
         });

        Thread.sleep(500);
    }
}

■実行結果

RxComputationThreadPool-1 onNext -> Hello, World!-1
RxComputationThreadPool-1 onNext -> Hello, World!-2
RxComputationThreadPool-1 onNext -> Hello, World!-3
RxComputationThreadPool-1 onComplete


もっとシンプルに試すだけなら以下のコードとかでもいいかもしれないですね

■サンプルコード

import io.reactivex.Flowable;

public class HelloWorldRx2 {

    public static void main(String[] args) throws Exception {
        Flowable<String> flowable =
                Flowable.just("Hello, World!-1", "Hello, World!-2", "Hello, World!-3");

        flowable.subscribe(data -> System.out.println(data));

        Thread.sleep(500);
    }
}

■実行結果

Hello, World!-1
Hello, World!-2
Hello, World!-3


とりあえず、RxJavaを使ったHello Worldができました



関連書籍

RxJavaリアクティブプログラミング (CodeZine BOOKS)

RxJavaリアクティブプログラミング (CodeZine BOOKS)

Go言語 - HTMLテンプレートの使い方

GolangでのHTMLテンプレート記述方法や値の展開方法等について学ぶために、html/templateを試してみました。


変数をそのまま展開

コード内の変数をhtmlのテンプレートに展開する例です

■htmlテンプレート(template000.html.tpl)

<!DOCTYPE html>
<html>
<body>
    msg: {{.}} 
</body>
</html>

■サンプルコード

package trial

import (
    "html/template"
    "log"
    "net/http"
)

func htmlHandler0(w http.ResponseWriter, r *http.Request) {
    // テンプレートをパース
    t := template.Must(template.ParseFiles("templates/template000.html.tpl"))

    str := "Sample Message"

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template000.html.tpl", str); err != nil {
        log.Fatal(err)
    }
}

func main() {
    http.HandleFunc("/page0", htmlHandler0)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144443p:plain


mapの値を展開

mapをhtmlのテンプレートに展開する例です

■htmlテンプレート(template001.html.tpl)

<!DOCTYPE html>
<html>
<body>
    key1: {{.key1}}, key2: {{.key2}}, key3: {{.key3}} 
</body>
</html>

■サンプルコード

package trial

import (
    "html/template"
    "log"
    "net/http"
)

func htmlHandler1(w http.ResponseWriter, r *http.Request) {
    // テンプレートをパース
    t := template.Must(template.ParseFiles("templates/template001.html.tpl"))

    m := map[string]int{
        "key1": 101,
        "key2": 202,
        "key3": 303,
        "key4": -404,
    }

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template001.html.tpl", m); err != nil {
        log.Fatal(err)
    }
}

func main() {
    http.HandleFunc("/page1", htmlHandler1)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144531p:plain


構造体を展開

構造体をhtmlのテンプレートに展開する例です

■htmlテンプレート(template002.html.tpl)

<!DOCTYPE html>
<html>
<body>
    Name: {{ .Name }}, Age: {{ .Age }} 
</body>
</html>

■サンプルコード

package trial

import (
    "html/template"
    "log"
    "net/http"
)

func htmlHandler2(w http.ResponseWriter, r *http.Request) {

    t := template.Must(template.ParseFiles("templates/template002.html.tpl"))

    type SampleData struct {
        Name string
        Age  int
    }

    data := SampleData{Name: "Taro", Age: 25}

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template002.html.tpl", data); err != nil {
        log.Fatal(err)
    }
}

func main() {
    http.HandleFunc("/page2", htmlHandler2)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144541p:plain


関数を実行

htmlのテンプレート内で関数呼び出しを行う例です

■htmlテンプレート(template003.html.tpl)

<!DOCTYPE html>
<html>
<body>
    Func1 -> {{ samplefunc1 }}
<br/>
    Func2 -> {{.msg1 | samplefunc2}}
<br/>
    Func3 -> {{.msg2 | samplefunc3}}
</body>
</html>

■サンプルコード

package trial

import (
    "fmt"
    "html/template"
    "log"
    "net/http"
    "strings"
    "time"
)

func htmlHandler3(w http.ResponseWriter, r *http.Request) {

    funcMap := template.FuncMap{
        "samplefunc1": func() string { return time.Now().String() },
        "samplefunc2": func(src string) string { return fmt.Sprintf("[$$ %s $$]", src) },
        "samplefunc3": strings.ToUpper,
    }
    // テンプレートをパース
    t := template.Must(template.New("t").Funcs(funcMap).ParseFiles("templates/template003.html.tpl"))

    m := map[string]string{
        "msg1": "golang is programming language.",
        "msg2": "hello template app.",
    }

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template003.html.tpl", m); err != nil {
        log.Fatal(err)
    }
}

func main() {
    http.HandleFunc("/page3", htmlHandler3)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144551p:plain


ループ

htmlのテンプレート内でループを行う例です

■htmlテンプレート(template004.html.tpl)

<!DOCTYPE html>
<html>
<body>
    <div>
        <h4>DataList</h4>
        {{range .}}
        <p>{{.}}</p>
        {{end}}
    </div>
</body>
</html>

■サンプルコード

package trial

import (
    "html/template"
    "log"
    "net/http"
)

func htmlHandler4(w http.ResponseWriter, r *http.Request) {

    t := template.Must(template.ParseFiles("templates/template004.html.tpl"))

    strArray := []string{"aa", "bbb", "ccc", "dddd"}

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template004.html.tpl", strArray); err != nil {
        log.Fatal(err)
    }
}

func main() {
    http.HandleFunc("/page4", htmlHandler4)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144607p:plain


リクエストパラメータをテンプレートにセットして表示

htmlのテンプレート内で受け取ったリクエストパラメータを反映する例です

■htmlテンプレート(template005.html.tpl)

<!DOCTYPE html>
<html>
<body>
<div>
  Request Param1 -> {{.Param1 |safehtml}}
</div>
<div>
  Request Param2 -> {{.Param2 |safehtml}}
</div>
</body>
</html>

■サンプルコード

package trial

import (
    "html/template"
    "log"
    "net/http"
)

func htmlHandler5(w http.ResponseWriter, r *http.Request) {
    funcMap := template.FuncMap{
        "safehtml": func(text string) template.HTML { return template.HTML(text) },
    }
    t := template.Must(template.New("T").Funcs(funcMap).ParseFiles("templates/template005.html.tpl"))

    st := struct {
        Param1 string
        Param2 string
    }{
        Param1: r.FormValue("param1"),
        Param2: r.FormValue("param2"),
    }

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "template005.html.tpl", st); err != nil {
        log.Fatal(err)
    }
}

func LoadTemplate() {
    http.HandleFunc("/page5", htmlHandler5)

    // サーバーを起動
    http.ListenAndServe(":8989", nil)
}


■ブラウザでのアクセス結果

f:id:nini_y:20170702144616p:plain



関連エントリ

Lombok - @Builderでデフォルト値を指定する

Lombok@Builderアノテーションは何かと便利なのですが、プロパティへ値をセットするためのBuilderのメソッドを呼ばないと対応するフィールドが初期値(数値なら0, booleanならfalse, オブジェクトならnull)になってしまいます。

このあたりの動きをv1.16.16で増えた@Builder.Defaultで改善できるようになっています。


Builderで値をセットしなかった時の動きの確認

動作確認用にPerson1というクラスを用意して@Builderを付与します。
Person1をBuilderを通じて2回生成します。1回目は全プロパティをセットしますが2回目はnameプロパティに値をセットしません。

import lombok.Builder;
import lombok.Value;

@Builder
@Value
public class Person1 {

    private final long id;

    private final String name;

    private final String description;
}


■呼び出し側のコード

public final class Person1Client {

    public static void main(String[] args) {
        Person1 p1a = Person1.builder()
                .id(10L)
                .name("Taro")
                .description("name set person.")
                .build();

        System.out.println("p1a#toString: " + p1a);

        // nameメソッドを呼ばない
        Person1 p1b = Person1.builder()
                .id(20L)
                .description("name not set person.")
                .build();

        System.out.println("p1b#toString: " + p1b);
    }
}


■実行結果

結果としてPerson1生成の2回目の呼び出しではnameの値がnullになっています。

p1a#toString: Person1(id=10, name=Taro, description=name set person.)
p1b#toString: Person1(id=20, name=null, description=name not set person.)

対象オブジェクトの生成側が全プロパティをセットするのが必須ならいいですが、そうでないケースもあると思います。
そういう場合にはセットしなかったプロパティにはデフォルト値を入れておきたいところです。


デフォルト値をセットしたい時は

デフォルト値を入れておきたいという要望に応えるための方法として若干裏技的に以下リンク先のような方法が存在しています。

@Builderアノテーションによって生み出されるコードを逆手に取ったような手法になっています。


@Builder.Default によるデフォルト値セット

Lombokのv1.16.16から@Builderでのデフォルト値指定のための@Builder.Defaultが追加されました

デフォルト値をセットしたいフィールドに@Builder.Defaultアノテーションを付与して、フィールドにデフォルト値をセットします。

本サンプルではnameフィールドに "<UNKNOWN>"という値をデフォルト値としてセットします

import lombok.Builder;
import lombok.Value;

@Builder
@Value
public class Person2 {

    private final long id;

    @Builder.Default
    private final String name = "<UNKNOWN>";

    private final String description;
}


■呼び出し側のコード

public final class Person2Client {

    public static void main(String[] args) {
        Person2 p2a = Person2.builder()
                .id(10L)
                .name("Taro")
                .description("name set person.")
                .build();

        System.out.println("p1a#toString: " + p2a);


        // nameメソッドを呼ばない
        Person2 p2b = Person2.builder()
                .id(20L)
                .description("name not set person.")
                .build();

        System.out.println("p2b#toString: " + p2b);
    }
}


■実行結果

結果を見ると、Builderのnameメソッドを呼ばない場合はデフォルト値(<UNKNOWN>)がセットされていることが分かります

p1a#toString: Person2(id=10, name=Taro, description=name set person.)
p2b#toString: Person2(id=20, name=<UNKNOWN>, description=name not set person.)


補足

実は私がこのエントリ書いてる段階では、IntelliJのIDE上では、@Builder.Defaultを付与したフィールドへBuilderで値をセットするメソッドがコンパイルエラーになってしまいました。
ただし、ビルドや実行はできるので、Lombokのpluginが対応できていないだけなのかもしれません。

EclipseであればIDE上も何もエラー出ませんでした。



関連エントリ

Go言語 - XMLを読んで特定の要素を削って出力

Golangではencoding/xmlパッケージでXMLを簡単に扱えそうなので、Hello World的に試してみました。

今回のエントリでは、XMLファイルを読んで特定のエレメントを削ったXMLを再出力するということをやってみました。
(といっても、Unmarshal(XML⇒構造体への変換)する構造体に対象要素に紐づくメンバを用意しないだけで実現できます)


前提

今回は以下のようなXMLが書かれたファイルがあるとして、その中からdescriptionというエレメントを除外したいとします

<?xml version="1.0" encoding="UTF-8"?>
<servers>
  <server>
    <id>s0001</id>
    <name>サーバA-001</name>
    <port>443</port>
    <description>試験用のサーバです</description>
    <subscriber_id>sub000002</subscriber_id>
    <contract_period>
      <start_date>2017-10-01</start_date>
      <end_date>2021-09-30</end_date>
    </contract_period>
  </server>
  <server>
    <id>s0002</id>
    <name>サーバA-002</name>
    <port>8080</port>
    <description>ECサイトで利用するための</description>
    <subscriber_id>sub000001</subscriber_id>
    <contract_period>
      <start_date>2012-04-01</start_date>
      <end_date>2024-03-31</end_date>
    </contract_period>
  </server>
  <server>
    <id>s0003</id>
    <name>サーバA-002</name>
    <port>8080</port>
    <description>利用用途未定</description>
    <subscriber_id>sub000003</subscriber_id>
    <contract_period>
      <start_date>2015-04-01</start_date>
      <end_date>2019-03-31</end_date>
    </contract_period>
  </server>
</servers>


Golang側のコードでは、上記XMLをUnmarshalして紐づける構造体を以下のように用意しました。
(server構造体に、descriptionのエレメントを紐づける変数がありません)

除外する要素以外は構造体のタグで紐づけを行っています

type ServerList struct {
    XMLName xml.Name `xml:"servers"`
    Svs     []server `xml:"server"`
}

type server struct {
    XMLName                   xml.Name `xml:"server"`
    ID                        string   `xml:"id"`
    ServerName                string   `xml:"name"`
    Port                      string   `xml:"port"`
    SubscriberID              string   `xml:"subscriber_id"`
    ContractStartDate         string   `xml:"contract_period>start_date"`
    ContractEndDate           string   `xml:"contract_period>end_date"`
}


実行サンプル

以下XMLファイルを読んで、エレメント削って出力するGoのプログラムです。
読み込むXMLファイルをコマンドラインで -f パラメータに与えて、エレメントを除外した結果を標準出力に出力するようにしています。
(そのため、結果をリダイレクトれば結果をファイルに出力できます)

package main

import (
    "encoding/xml"
    "flag"
    "fmt"
    "io/ioutil"
    "os"
)

type ServerList struct {
    XMLName xml.Name `xml:"servers"`
    Svs     []server `xml:"server"`
}

type server struct {
    XMLName                   xml.Name `xml:"server"`
    ID                        string   `xml:"id"`
    ServerName                string   `xml:"name"`
    Port                      string   `xml:"port"`
    SubscriberID              string   `xml:"subscriber_id"`
    ContractStartDate         string   `xml:"contract_period>start_date"`
    ContractEndDate           string   `xml:"contract_period>end_date"`
}

func main() {
    var (
        srcXML string
    )

    flag.StringVar(&srcXML, "f", "servers.xml.", "src xml path.")

    flag.Parse()

    file, err := os.Open(srcXML)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    defer file.Close()
    data, err := ioutil.ReadAll(file)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    serverList := ServerList{}
    err = xml.Unmarshal(data, &serverList)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }

    output, err := xml.MarshalIndent(serverList, "  ", "    ")
    if err != nil {
        fmt.Printf("error: %v\n", err)
    }
    os.Stdout.Write([]byte(xml.Header))
    os.Stdout.Write(output)
}


ビルド(go build)後に以下のようにコマンドを実行(original.xmlが元のXMLファイル)

xmlconv.exe -f original.xml > dest.xml


結果、dest.xmlには以下のような内容が出力されています(descriptionというエレメントが除外されています)

<?xml version="1.0" encoding="UTF-8"?>
<servers>
    <server>
        <id>s0001</id>
        <name>サーバA-001</name>
        <port>443</port>
        <subscriber_id>sub000002</subscriber_id>
        <contract_period>
            <start_date>2017-10-01</start_date>
            <end_date>2021-09-30</end_date>
        </contract_period>
    </server>
    <server>
        <id>s0002</id>
        <name>サーバA-002</name>
        <port>8080</port>
        <subscriber_id>sub000001</subscriber_id>
        <contract_period>
            <start_date>2012-04-01</start_date>
            <end_date>2024-03-31</end_date>
        </contract_period>
    </server>
    <server>
        <id>s0003</id>
        <name>サーバA-002</name>
        <port>8080</port>
        <subscriber_id>sub000003</subscriber_id>
        <contract_period>
            <start_date>2015-04-01</start_date>
            <end_date>2019-03-31</end_date>
        </contract_period>
    </server>
</servers>


というわけで簡単にXMLファイルを扱うことができました