CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

testland/language-native-sast

Use when a repo needs security linting inside its own toolchain: Bandit for Python, gosec for Go, eslint-plugin-security and no-unsanitized for JavaScript/TypeScript, or PMD's Apex rules for Salesforce, with justified suppressions, baselines for legacy code, SARIF and CI gates.

Quality

87%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
language-native-sast
description:
Use when a repo needs security linting inside its own toolchain: Bandit for Python, gosec for Go, eslint-plugin-security and no-unsanitized for JavaScript/TypeScript, or PMD's Apex rules for Salesforce, with justified suppressions, baselines for legacy code, SARIF and CI gates.

language-native-sast

Overview

Every major ecosystem ships a first-party security linter that runs inside the toolchain developers already use - no scanner server, no new binary in the inner loop. This family trades depth for adoption cost: rules are single-file and pattern-based, but the feedback lands in the editor and on every commit, which is where shallow bugs (hardcoded creds, shell=True, innerHTML =, dynamic SOQL) actually get fixed.

LanguageToolReference
PythonBandit (PyCQA)references/bandit.md
Gogosec (securego), via golangci-lintreferences/gosec.md
JS / TSeslint-plugin-security + eslint-plugin-no-unsanitizedreferences/eslint-security.md
Salesforce ApexPMD category/apex/security.xmlreferences/pmd-apex.md

All four follow the same adoption pattern (below). The worked example uses the ESLint pair - the biggest audience; the per-tool references carry the full flag/rule catalogs, verified against each tool's docs.

Differentiation: these linters are single-file and (mostly) syntactic. For cross-language pattern rules use semgrep-rules; for cross-file taint tracking use codeql-queries; to merge their findings with other scanners into one gate use multi-tool-finding-triage.

When to use

  • The repo is Python, Go, JS/TS, or Apex and the team wants shift-left security feedback in the editor and on every commit, not only in a scheduled CI scan.
  • CI needs a fast, PR-blocking security lint pass without adopting a scanner server or a new SaaS.
  • A legacy codebase is adopting security linting and pre-existing findings must not block every PR (baseline-diff pattern, Step 3).

The pattern

Each tool's exact commands live in its reference; the workflow is shared.

Step 1 - Install as a dev dependency

The linter installs through the ecosystem's own package manager - it versions, caches, and updates like any other dev dependency:

pip install bandit[toml]                        # Python
go install github.com/securego/gosec/v2/cmd/gosec@latest   # Go
npm install --save-dev eslint-plugin-security eslint-plugin-no-unsanitized  # JS/TS
# Apex: PMD zip / Docker image (Java 8+) - see references/pmd-apex.md

Step 2 - First scan, then filter the noise

Run the recursive scan, then immediately narrow with the tool's severity/confidence filters - every tool in this family is noisy at default settings, and an unfiltered first run is how teams end up disabling the linter:

bandit -r . --severity-level=medium --confidence-level=medium
gosec -severity=high -confidence=high ./...
npx eslint "src/**/*.{js,ts}"      # rules pre-scoped by the shared config
pmd check -d . -R category/apex/security.xml --minimum-priority 2

Step 3 - Baseline-diff adoption for legacy code

On a legacy codebase, capture the current findings once and gate only on NEW findings, so the debt is tracked without blocking every PR:

  • Bandit: bandit -r . -f json -o old-findings.json, then bandit -r . --baseline old-findings.json in CI.
  • The generic pattern for the other tools: run the scan on main, save the report, diff per-PR findings against it and fail only on additions (multi-tool-finding-triage implements this diff + waiver flow).
  • Re-baseline on a schedule; persisted findings need waiver entries.

Step 4 - Suppression with justification (MANDATORY)

Every tool has an inline suppression syntax; every suppression carries a reason, reviewer, and expiry, audited quarterly:

ToolInline syntax
Bandit# nosec B602 (always with the rule ID)
gosec// #nosec G401 -- Reason: ...
ESLint// eslint-disable-next-line security/detect-object-injection + reason comment
PMD Apex@SuppressWarnings('PMD.ApexCRUDViolation') + reason comment

Bare suppressions (no rule ID, no reason) are unreviewed debt - grep for them in the quarterly audit. Categorical noise (a rule that can never apply) belongs in the tool's config file, not scattered inline.

Step 5 - CI wiring: SARIF + exit-code gate

All four tools emit SARIF for GitHub Code Scanning and gate CI via exit code. The shape is identical per tool - scan, upload SARIF if: always(), let the exit code block the PR:

- run: bandit -r . -f sarif -o bandit.sarif        # or gosec / eslint / pmd
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: bandit.sarif }

JSON output from each tool feeds multi-tool-finding-triage for the cross-scanner dedupe + waiver gate.

Worked example - ESLint security pair (JS/TS)

Per github.com/eslint-community/eslint-plugin-security and github.com/mozilla/eslint-plugin-no-unsanitized:

// eslint.config.js (flat config, ESLint 8.23+ / 9+)
import pluginSecurity from "eslint-plugin-security";
import nounsanitized from "eslint-plugin-no-unsanitized";

export default [
  pluginSecurity.configs.recommended,   // all 14 detect-* rules
  nounsanitized.configs.recommended,    // method + property DOM-sink rules
];

A scan of this code:

const userData = req.body;
element.innerHTML = userData.bio;             // nounsanitized/property
const file = fs.readFileSync(req.query.path); // security/detect-non-literal-fs-filename

reports both findings; the safe rewrites are element.textContent = ... (or DOMPurify for rich HTML) and an allowlist check on the path. The highest-volume false positive is security/detect-object-injection (any obj[key] access) - triage it per Step 4 or downgrade it to warn in config. SARIF output uses the scoped formatter name in full:

npx eslint --format @microsoft/eslint-formatter-sarif \
  --output-file eslint-security.sarif "src/**/*.{js,ts}"

ESLint exit codes per eslint.org/docs/latest/use/command-line-interface: 0 clean, 1 errors (the gate), 2 config error. Full rule tables, the legacy-config variant, and the complete two-pass CI workflow (JSON for the triager + SARIF for Code Scanning) are in references/eslint-security.md.

Anti-patterns

Anti-patternWhy it failsFix
Unfiltered first scan on a legacy repoNoise overwhelms; team disables the linterSeverity/confidence filters (Step 2) + baseline (Step 3)
Bare suppression without rule IDSuppresses ALL rules on that lineAlways name the rule (Step 4)
Suppression without reason + expiryPermanent unreviewed debtJustification template, quarterly audit (Step 4)
Running the linter only in scheduled CILoses the in-editor shift-left benefitDev-dependency install + pre-commit (Step 1)
Treating linter pass as full SAST coverageSingle-file, mostly syntactic analysisLayer semgrep-rules / codeql-queries

Limitations

  • Single-file analysis: none of the four track taint across module or package boundaries (gosec's SSA taint tracking is in-package). Use codeql-queries for interprocedural flows.
  • Rule depth varies by ecosystem era - Bandit is thinner on FastAPI/async patterns, gosec on generics, PMD Apex does not parse Lightning Web Components (use semgrep-rules for LWC JavaScript).
  • High-volume false-positive rules exist in each tool (detect-object-injection, G104, B101, ApexCRUDViolation on Visualforce getters) - the per-tool references name them and the sanctioned handling.

References

  • references/bandit.md - Bandit: rules, flags, pyproject config, pre-commit, baseline
  • references/gosec.md - gosec: G-rule catalog, golangci-lint integration, #nosec syntax
  • references/eslint-security.md - ESLint pair: 14+2 rule tables, SARIF formatter, CI workflow
  • references/pmd-apex.md - PMD Apex: 10-rule security category, custom rulesets, incremental cache
  • semgrep-rules, codeql-queries, sonarqube-rules - deeper / cross-language SAST siblings
  • multi-tool-finding-triage - cross-scanner dedupe + waiver gate
Workspace
testland
Visibility
Public
Created
Last updated
Source
GitHub Actions
Repository
https://github.com/testland/qaVerified
Badge
testland/language-native-sast badge