Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
285 lines (223 sloc) 7.23 KB

Secretlint Rule

Secretlint Rule is a npm package.

Requirements

  • export default ruleCreatorFunction
    • ruleCreatorFunction should have meta, messages, and create function
  • Package name should start with secretlint-rule-* or @scope/secretlint-rule-*

Example: @secretlint/secretlint-rule-example

@secretlint/secretlint-rule-example is an example package.

This secretllint rule just do following:

  • Found "secret" word and report it

Implementation(TypeScript version):

  • @secretlint/types package includes type definition for secretlint rule
import { SecretLintRuleCreator, SecretLintSourceCode } from "@secretlint/types";

// MessageIds
export const messages = {
    EXAMPLE_MESSAGE: {
        en: "found secret: {{ID}}",
        ja: "secret: {{ID}} がみつかりました"
    }
};

export const creator: SecretLintRuleCreator = {
    messages,
    meta: {
        // rule.meta.id should be same with package.json name
        id: "@secretlint/secretlint-rule-example",
        recommended: false,
        // type
        type: "scanner",
        // support content types: "text" or "binary" or "all"
        supportedContentTypes: ["text"],
        // Documentation Base URL for the package
        docs: {
            url:
                "https://github.com/secretlint/secretlint/blob/master/packages/%40secretlint/secretlint-rule-example/README.md"
        }
    },
    // Rule Logic
    create(context) {
        // Create Traslate instance
        const t = context.createTranslator(messages);
        return {
            // source has `content`, `filePath` etc...
            file(source: SecretLintSourceCode) {
                const pattern = /secret/gi;
                let match;
                while ((match = pattern.exec(source.content)) !== null) {
                    const index = match.index || 0;
                    const matchString = match[0] || "";
                    const range = [index, index + matchString.length];
                    // Report found "secret"
                    context.report({
                        // Replace "found secret: {{ID}}" with `ID` data
                        message: t("EXAMPLE_MESSAGE", {
                            ID: matchString
                        }),
                        range
                    });
                }
            }
        };
    }
};
// export it as default
export default creator;

Test: @secretlint/secretlint-rule-example

Secretlint testing is based on Snapshot testing like Jest.

Secretlint provide @secretlint/tester for testing. It will help you to write snapshot testing for your rule.

It is template for testing.

test/index.test.ts

import { snapshot } from "@secretlint/tester";
import path from "path";
import rule from "../src/index";

describe("@secretlint/secretlint-rule-basicauth", () => {
    snapshot({
        // Base Config
        // You can override by each snapshot
        defaultConfig: {
            rules: [
                {
                    id: require("../package.json").name,
                    rule,
                    options: {}
                }
            ]
        },
        updateSnapshot: !!process.env.UPDATE_SNAPSHOT,
        snapshotDirectory: path.join(__dirname, "snapshots")
    }).forEach((name, test) => {
        it(name, async function() {
            const status = await test();
            if (status === "skip") {
                this.skip();  // It call mocha's skip, you can anothor testing framework
            }
        });
    });
});

Creating test case

  • <snapshots_dir>/<test_case_name>/
    • input.* is input file (any extension)
    • output.json is output file that is created by @secretlint/tester
test/
├── index.test.ts
├── mocha.opts
├── snapshots
│   ├── ng.secret
│   │   ├── input.txt
│   │   └── output.json
│   └── ok.valid
│       ├── input.txt
│       └── output.json
└── tsconfig.json

Run Test

$ npx mocha "test/**/*.{js,ts}"

Update snapshots

$ UPDATE_SNAPSHOT=1 npx mocha "test/**/*.{js,ts}"

For example, test/snapshots/ng.secret/input.txt has following content

THIS IS SECRET.

The output.json for ng.secret/input.txt is following json.

{
    "filePath": "[SNAPSHOT]/ng.secret/input.txt",
    "messages": [
        {
            "message": "found secret: SECRET",
            "range": [
                8,
                14
            ],
            "ruleId": "@secretlint/secretlint-rule-example",
            "loc": {
                "start": {
                    "line": 1,
                    "column": 8
                },
                "end": {
                    "line": 1,
                    "column": 14
                }
            },
            "severity": "error",
            "messageId": "EXAMPLE_MESSAGE",
            "docsUrl": "https://github.com/secretlint/secretlint/blob/master/packages/%40secretlint/secretlint-rule-example/README.md#EXAMPLE_MESSAGE",
            "data": {
                "ID": "SECRET"
            }
        }
    ]
}

If you ok with the output, please commit it. If you not ok with the output, fix rule and update snapshots again.

Publishing

If you want to publish your secretlint rule, see following documents.

Package Naming Conventions

Secretlint rule package naming should have textlint-rule- prefix.

  • secretlint-rule-<name>
  • @scope/textlint-rule-<name>

Example: secretlint-rule-example

Secretlint user use it following:

{
    "rules": [
      { 
        "id": "secretlint-rule-example"
      }
    ]
}

Example: @scope/secretlint-rule-example

Secretlint user use it following:

{
    "rules": [
      {
        "id": "@scope/secretlint-rule-example"
      }
    ]
}

Rule Naming Conventions

The rule naming conventions for secretlint are simple:

  • If your rule is disallowing something, prefix it with no-.
    • For example, no-todo disallowing TODO: and no-exclamation-question-mark for disallowing ! and ?.
  • If your rule is enforcing the inclusion of something, use a short name without a special prefix.
    • If the rule for english, please uf secretlint-rule-en- prefix.
  • Keep your rule names as short as possible, use abbreviations where appropriate.
  • Use dashes(-) between words.

For more details, please see following:

Performance

Use --profile option for checking performance of your rule .

$ secretlint --profile "**/*"

Tips

Add Word boundary

For example, Use RegExp for finding token-like string:

/(?:ACCESS|access|Access)_?(?:KEY|key|Key)[:=]([A-Za-z0-9/\+=]{12})/

It will work, but it has some false-positive.

Because, It will match a token string that longer than 40 characters like follows.

ACCESS_KEY=ENCTRYPEDENCTRYPEDENCTRYPEDTOKEN

Litte patch is adding Word boundary \b pattern.

/(?:ACCESS|access|Access)_?(?:KEY|key|Key)[:=]([A-Za-z0-9/\+=]{12})\b/
You can’t perform that action at this time.