diff --git a/internal/parser/cppcheck/parser.go b/internal/parser/cppcheck/parser.go
index 0630d38..514eb56 100644
--- a/internal/parser/cppcheck/parser.go
+++ b/internal/parser/cppcheck/parser.go
@@ -9,10 +9,8 @@ import (
 	"github.com/joint-online-judge/JOJ3/internal/stage"
 )
 
-type CppCheck struct{}
-
 type Match struct {
-	Severity []string
+	Keywords []string
 	Score    int
 }
 
@@ -32,6 +30,8 @@ type Record struct {
 	Id       string `json:"id"`
 }
 
+type CppCheck struct{}
+
 func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult {
 	// stdout := executorResult.Files[conf.Stdout]
 	stderr := executorResult.Files[conf.Stderr]
diff --git a/internal/parser/cppcheck/score.go b/internal/parser/cppcheck/score.go
index 9b30b1c..babdfe9 100644
--- a/internal/parser/cppcheck/score.go
+++ b/internal/parser/cppcheck/score.go
@@ -2,7 +2,7 @@ package cppcheck
 
 import (
 	"fmt"
-	"log/slog"
+	"strings"
 )
 
 type Severity int
@@ -18,52 +18,18 @@ const (
 	UNKNOWN
 )
 
-func severityFromString(severityString string) (Severity, error) {
-	switch severityString {
-	case "error":
-		return ERROR, nil
-	case "warning":
-		return WARNING, nil
-	case "portability":
-		return PORTABILITY, nil
-	case "performance":
-		return PERFORMANCE, nil
-	case "style":
-		return STYLE, nil
-	case "information":
-		return INFORMATION, nil
-	case "debug":
-		return DEBUG, nil
-	default:
-		return UNKNOWN, fmt.Errorf("unknown severity type \"%s\" for cppcheck", severityString)
-	}
-}
-
 func GetResult(records []Record, conf Conf) (string, int, error) {
 	result := "### Test results summary\n\n"
 	var severityCounts [UNKNOWN + 1]int
-	var severityScore [UNKNOWN + 1]int
 	score := conf.Score
-
-	for _, match := range conf.Matches {
-		severities := match.Severity
-		score := match.Score
-		for _, severityString := range severities {
-			severity, err := severityFromString(severityString)
-			if err != nil {
-				return "", 0, err
-			}
-			severityScore[int(severity)] = score
-		}
-	}
-
 	for _, record := range records {
-		severity, err := severityFromString(record.Severity)
-		if err != nil {
-			slog.Error("parse severity", "error", err)
+		for _, match := range conf.Matches {
+			for _, keyword := range match.Keywords {
+				if strings.Contains(record.Id, keyword) {
+					score -= match.Score
+				}
+			}
 		}
-		severityCounts[int(severity)] += 1
-		score -= severityScore[int(severity)]
 	}
 	result += fmt.Sprintf("1. error: %d\n", severityCounts[0])
 	result += fmt.Sprintf("2. warning: %d\n", severityCounts[1])
diff --git a/internal/parser/cpplint/parser.go b/internal/parser/cpplint/parser.go
index 7c448f5..bc7abdb 100644
--- a/internal/parser/cpplint/parser.go
+++ b/internal/parser/cpplint/parser.go
@@ -2,27 +2,33 @@ package cpplint
 
 import (
 	"fmt"
-	"log/slog"
 	"regexp"
-	"strconv"
 	"strings"
 
 	"github.com/joint-online-judge/JOJ3/internal/stage"
 	"github.com/joint-online-judge/JOJ3/pkg/utils"
 )
 
+type Match struct {
+	Keywords []string
+	Score    int
+}
+
 type Conf struct {
-	Score int
+	Score   int
+	Matches []Match
+	Stdout  string `default:"stdout"`
+	Stderr  string `default:"stderr"`
 }
 
 type Cpplint struct{}
 
 func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult {
-	stderr := executorResult.Files["stderr"]
+	stderr := executorResult.Files[conf.Stderr]
 	pattern := `(.+):(\d+):  (.+)  \[(.+)\] \[(\d)]\n`
 	re := regexp.MustCompile(pattern)
 	matches := re.FindAllStringSubmatch(stderr, -1)
-	score := 0
+	score := conf.Score
 	comment := "### Test results summary\n\n"
 	categoryCount := map[string]int{}
 	for _, match := range matches {
@@ -37,15 +43,21 @@ func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult {
 		// }
 		// message := match[3]
 		category := match[4]
-		confidence, err := strconv.Atoi(match[5])
-		if err != nil {
-			slog.Error("parse confidence", "error", err)
-			return stage.ParserResult{
-				Score:   0,
-				Comment: fmt.Sprintf("Unexpected parser error: %s.", err),
+		// confidence, err := strconv.Atoi(match[5])
+		// if err != nil {
+		// 	slog.Error("parse confidence", "error", err)
+		// 	return stage.ParserResult{
+		// 		Score:   0,
+		// 		Comment: fmt.Sprintf("Unexpected parser error: %s.", err),
+		// 	}
+		// }
+		for _, match := range conf.Matches {
+			for _, keyword := range match.Keywords {
+				if strings.Contains(category, keyword) {
+					score -= match.Score
+				}
 			}
 		}
-		score -= confidence
 		parts := strings.Split(category, "/")
 		if len(parts) > 0 {
 			category := parts[0]