2022年12月28日水曜日

Ruby でライセンスチェックをする方法

Ruby でライセンスチェックをする方法

概要

licensed を使うとソースコードが使用しているライセンスをチェックすることができます
基本的にはコードがライセンスに準拠していない場合には対応して対応が完了したらライセンスチェックをスルーするさせるようなツールになります

環境

  • macOS 11.7.2
  • Ruby 3.1.2
    • licensed 3.9.1

licensed のインストール

  • gem install licensed

設定ファイルの作成 .licensed.yml

設定ファイルを作成します
ライセンスチェック対象のファイルとソースコードの言語を設定します

allowed はチェック時に成功とするライセンスを記載します
自分のソースコードの使い方、ライセンス表記で条件を満たしているライセンスの一覧を記載することになります

ignored はライセンスチェックを無視する gem を記載します
ライセンス表記がない gem などがあるのでその場合は ignored に記載します

  • vim .licensed.yml
sources:
  bundler: true
apps:
  - source_path: ./libs
  - source_path: ./controllers
  - source_path: ./views
allowed:
  - mit
  - apache-2.0
  - other
  - none
ignored:
  bundler:
    - bundler
    - ruby2_keywords

キャッシュファイルの作成

まずはキャッシュファイルを作成する必要があります
作成が成功すると .licenses/ ディレクトリが作成されます

  • licensed cache

ライセンスチェック

最後にライセンスチェックをします

  • licensed status
............F.................................................FF..........................
Errors:
* views.bundler.e2mmap
  version: 0.1.0, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/e2mmap.dep.yml, license: bsd-2-clause, allowed: false
    - license needs review: bsd-2-clause

* views.bundler.reverse_markdown
  version: 2.1.1, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/reverse_markdown.dep.yml, license: wtfpl, allowed: false
    - license needs review: wtfpl

* views.bundler.rexml
  version: 3.2.5, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/rexml.dep.yml, license: bsd-2-clause, allowed: false
    - license needs review: bsd-2-clause


90 dependencies checked, 3 errors found.

Licensed found errors during source enumeration.  Please see https://github.com/github/licensed/tree/master/docs/commands/status.md#status-errors-and-resolutions for possible resolutions.

最後に

あとは allowed に記載していないライセンスに準拠するようにソースコードやライセンス表記を修正したあとで allowed にライセンスを追加していく感じで OK です

参考サイト

2022年12月27日火曜日

licensee を使って gem のライセンス情報を取得してみる

licensee を使って gem のライセンス情報を取得してみる

概要

gem のライセンスを licensee を使って取得してみました
使用している gem のライセンスチェックをする際に使用できます

環境

  • macOS 11.7.2
  • Ruby 3.1.2
    • licensee 9.15.3
  • cmake 3.25.1

cmake のインストール

native extension で cmake に依存しているため事前にインストールしておきます

  • brew install cmake

licensee のインストール

  • gem install licensee

ライセンスチェック

  • licensee detect vendor/ruby/3.1.0/gems/sinatra-3.0.5
License:        MIT
Matched files:  LICENSE, sinatra.gemspec
LICENSE:
  Content hash:  4c2c763d64bbc7ef2e58b0ec6d06d90cee9755c9
  Attribution:   Copyright (c) 2007, 2008, 2009 Blake Mizerany
Copyright (c) 2010-2017 Konstantin Haase
Copyright (c) 2015-2017 Zachary Scott
  Confidence:    100.00%
  Matcher:       Licensee::Matchers::Exact
  License:       MIT
sinatra.gemspec:
  Confidence:  90.00%
  Matcher:     Licensee::Matchers::Gemspec
  License:     MIT

再帰的にチェックする

残念ながらコマンドラインから再帰的にチェックする機能はないので Ruby で直接チェックしましょう

require "licensee"

dirs = Dir.glob("./vendor/ruby/3.1.0/gems/*")

dirs.each do |dir|
  puts dir

  project = Licensee.project dir
  project.licenses.each do |license|
    puts license.key
  end

  # or
  # license = Licensee.license dir
  # puts license.key unless license.nil?
end

参考サイト

2022年12月26日月曜日

Gitlabにカスタムバッジを作成する方法

Gitlabにカスタムバッジを作成する方法

概要

デフォルトだとパイプラインやカバレッジなどのバッジは用意されています
これ以外のバッジを作成する場合は自分で作成する必要があります
今回はカスタムバッジを AnyBadge を使って作成する方法を紹介します

環境

  • Gitlab-ee 15.4.3-ee

.gitlab-ci.yml

流れとしてはまず CI 上でバッジ情報を作成します
そして作成したバッジをプロジェクトのバッジ設定から参照するようにする感じです

まずは .gitlab-ci.yml から作成します

stages:
    - build

create_badge_svg:
  stage: build
  image: python:3.10.9
  script:
    - echo "Python other dependencies installation"
  after_script:
    - pip install anybadge
    - d=$(date "+%Y/%m/%d")
    - anybadge -l "Today is" -v $d -f today.svg -c green
  artifacts:
    paths:
        - today.svg
    when: always
    expire_in: 4 weeks

ちょっと説明

簡単にやっていることを説明するとまず anybadge をインストールしています
すると anybadge というコマンドが使えるようになります
今回は日付の情報を表示するバッジを作成したいと思います
date コマンドで本日の日付を取得してその情報を anybadge コマンドに与えて svg ファイルを作成します
あとは作成した svg ファイルを artifacts で保存すれば OK です

プロジェクト側でバッジを参照する

Settings -> General -> Badges から新規でバッジを作成します

  • Name・・・なんでもOKです
  • Link・・・バッジをクリックしたときに飛ぶURLを記載します、これも何でもOKです
  • Badge Image URL・・・ここに gitlab-ci で作成したバッジの URL を記載します

サンプルは以下の通りです

svg を直接ダウンロードできる URL を指定する必要があります
今回は master ブランチの最新のジョブの svg ファイルを参照する API をコールすることで取得しています
他にも取得する方法はあるのでお好きな方法でアクセスしてください

動作確認

あとはバッジを追加してプロジェクトの先頭にいけばバッジがあることを確認できると思います

最後に

AnyBadge を使って Gitlab にカスタムバッジを作成する方法を紹介しました
今回は gitlab-ci と組み合わせて作成しましたが結局 svg ファイルがあればいいので無理に CI と組合わせる必要はありません

参考サイト

2022年12月23日金曜日

treemacs にアイコンを表示する方法

treemacs にアイコンを表示する方法

概要

treemacs-all-the-icons を使います

環境

  • macOS 11.7.2
  • emacs 28.1

インストール

package-list-packages -> treemacs-all-the-icons をインストール

.emacs

(when (display-graphic-p)
  (require 'all-the-icons))
(with-eval-after-load 'treemacs
  (require 'treemacs-all-the-icons)
  (treemacs-load-theme "all-the-icons"))

フォントのインストール

  • M-x all-the-icons-install-fonts

注意事項

emacs -nw では使えません

https://github.com/Alexander-Miller/treemacs/issues/53

最後に

デフォルトでも GUI モードの emacs であれば表示されますが足りないので all-the-icons を使います

参考サイト

2022年12月22日木曜日

Google App Script で使用している列の一覧を取得する方法

Google App Script で使用している列の一覧を取得する方法

概要

例えば A から AB まで列を広げている場合に A から AB までを持つ配列を取得する方法を紹介します

環境

  • Google App Script 2022/12/16 時点

サンプルコード

// 数字情報を列のアルファベットに変換するメソッド
function columnToLetter(column) {
  var temp, letter = '';
  while (column > 0)
  {
    temp = (column - 1) % 26;
    letter = String.fromCharCode(temp + 65) + letter;
    column = (column - temp - 1) / 26;
  }
  return letter;
}

// 指定の配列を number 個ずつ分割するメソッド
function sliceByNumber(array, number) {
  const length = Math.ceil(array.length / number)
  return new Array(length).fill().map((_, i) =>
    array.slice(i * number, (i + 1) * number)
  )
}

// メイン
function main() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getActiveSheet();
  var activeCols = sliceByNumber([...Array(sheet.getLastColumn()).keys()].map(i => columnToLetter(i+1)), 3);
}

ちょっと解説

[...Array(sheet.getLastColumn()).keys()] で最初の列からアクティブになっている最後の列までの数字配列を取得します

その結果に対して map で columnToLetter を実行し数字 -> アルファベットの変換をしています
数字状態の配列は0から始まっているので1から始めるために+1しています (A列は1に割り当てているので)

あとは sliceByNumber で配列を更に3つずつに分割しています
分割が必要ない場合は sliceByNumber の実行は不要です

最後に

これでどんどん列が増えていっても自動で列情報を取得することができます

2022年12月21日水曜日

dataclass を使って配列内にある辞書データをオブジェクトに変換する方法

dataclass を使って配列内にある辞書データをオブジェクトに変換する方法

概要

前回 dataclass を使った JSON <-> オブジェクトの変換方法を紹介しました
今回はその中でもクセのある辞書データが配列内に直接置かれている場合を紹介したいと思います

また配列内にある辞書データはデータ構造がデータごとに異なる場合を想定しています

環境

  • macOS 11.7.1
  • Python 3.10.2
  • dataclass-wizard 0.22.2

サンプルコード

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import (List,
                    Optional)


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Profile():
    lang: Optional[List[Lang]] = None
    framework: Optional[List[Framework]] = None


@dataclass
class UserData(JSONWizard):
    name: str
    profile: List[Profile]


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)

ちょっと解説

今回は profile というフィールドが配列でこの中にデータ構造の異なる辞書データが複数個入ることを想定しています

ポイントは Profile クラスでここにデータ構造が異なるデータをデフォルト None ですべて定義する点です

配列の場合順番にオブジェクトに変換されます 最初は lang 情報が変換されます
Profile クラスに渡されるデータは lang 情報だけなので必ず他のフィールドはデフォルト値で初期化しておく必要があります
lang 変換時には framework フィールドの情報は渡されないので None が設定されます

つまり配列内の最初のデータは lang がオブジェクトに変換され framework が None として変換されます

配列の2つ目の値は framework になります
framework が Profile クラスに渡されて変換されます
同様に framework 情報のみが渡されるので lang は None として変換されます

このように配列それぞれのデータごとに変換が行われるので配列内で管理するデータ構造分定義する必要がありまたそれぞれのフィールドはデフォルト値を必ず設定する必要があります

tuple でもいけます

あとから気づいたんですが tuple を使ってもいけます
この場合は tuple に定義する順番は配列内の順番と同じである必要があります
None もないのでこちらのほうが綺麗かもしれません

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import List


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Frameworks():
    framework: List[Framework]


@dataclass
class Langs():
    lang: List[Lang]


@dataclass
class UserData(JSONWizard):
    name: str
    profile: tuple[Langs, Frameworks]


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)

更に配列の順番が固定であればオブジェクトから直接参照できるようにできる

例えば今回のように 1 番目が lang 2 番目が framework のように配列に格納されている順番が保証されているのであればわざわざ profile からインデックスで参照せずに直接 lang と framework を参照してもいいと思います

その場合は __post_init__ と組み合わせるといい感じになると思います

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import (List,
                    Optional)


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Frameworks():
    framework: List[Framework]


@dataclass
class Langs():
    lang: List[Lang]


@dataclass
class Profile():
    lang: List[Lang]
    framework: List[Framework]


@dataclass
class UserData(JSONWizard):
    name: str
    profile: tuple[Langs, Frameworks]
    p: Optional[Profile] = None

    def __post_init__(self):
        self.p = Profile(self.profile[0].lang,
                         self.profile[1].framework)


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)
    print(result.p.lang[0].ruby)
    print(result.p.framework[0].sinatra)

最後に

オブジェクトに変換する場合は JSON の構造に合わせて型を定義する必要があるのでうまく行かない場合は型定義がちゃんと JSON から抽出できているかを確認しましょう

参考サイト

2022年12月20日火曜日

Trivy超入門

Trivy超入門

概要

Trivy はオープンソースの脆弱性チェックツールです
今回はインストールから簡単な実行方法まで紹介します

環境

  • Ubuntu 18.04
  • trivy 0.35.0

インストール

  • sudo apt-get install wget apt-transport-https gnupg lsb-release
  • wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
  • echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
  • sudo apt-get update
  • sudo apt-get install trivy

docker イメージのスキャン

  • trivy image python:3.10.2-buster

ローカルにあるファイルをスキャン

  • trivy fs --security-checks vuln,secret,config proj/path/to/secret.py
  • trivy fs --security-checks vuln,secret,config proj/path/to/docker
  • trivy fs --security-checks vuln,secret,config proj/path/to/Pipfilie.lock

シークレットなどを管理している python ファイルを指定したり Pipfile.lock や Dockerfile がある場所を指定します

再帰的にスキャンするには

どうやらないようです
https://github.com/aquasecurity/trivy/issues/1302

スキャン対象のファイルを特定して指定するしかないようです

最後に

Trivy を試してみました
インストールから実行するまではかなり簡単に試せるようです
基本的にはイメージにしてからそれをスキャンする形になると思います

がイメージによっては本質ではない ImageMagic やら関係のない CVE ばかり出てくるのでチェック対象を絞ってスキャンをする必要がありそうです

参考サイト