Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
14eb7c2
๐Ÿ›ก๏ธ Sentinel: [MEDIUM] Add missing input validation on primary arguments
seonghobae Jul 5, 2026
932ae97
I am responding to a critical Sentinel alert to fix a vector wipeout โ€ฆ
seonghobae Jul 6, 2026
1831d68
โšก Bolt: R/aFIPC.R์˜ ๋ฃจํ”„ ๋‚ด ํŠน์ • ๋ฌธํ•ญ ์ธ๋ฑ์Šค ํƒ์ƒ‰ ์บ์‹ฑ์„ ํ†ตํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™”
seonghobae Jul 7, 2026
044d178
๐Ÿ›ก๏ธ Sentinel: [MEDIUM] Fix Unhandled Exceptions on Model Estimation Faโ€ฆ
seonghobae Jul 7, 2026
e340009
โšก Bolt: R/aFIPC.R์˜ ๋ฃจํ”„ ๋‚ด ํŠน์ • ๋ฌธํ•ญ ์ธ๋ฑ์Šค ํƒ์ƒ‰ ์บ์‹ฑ์„ ํ†ตํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™”
seonghobae Jul 8, 2026
d406a36
Merge branch 'master' into feature branch
Jul 8, 2026
c623b3d
Merge branch 'master' into feature branch
Jul 8, 2026
d64dc87
Merge branch 'master' into feature branch
Jul 8, 2026
1c1ff02
chore: ignore dummy keys in packrat doc files
seonghobae Jul 8, 2026
b2a23c6
Merge branch 'master' into bolt/cache-which-indices-for-loop-14810454โ€ฆ
opencode-agent[bot] Jul 9, 2026
8724f05
test: preserve fixed-parameter Hessian stability
seonghobae Jul 9, 2026
6fab9cd
fix: reject spoofed mirt model inputs
seonghobae Jul 9, 2026
5602fbd
fix: keep model existence checks local
seonghobae Jul 9, 2026
84f979b
chore: exclude trivy YAML from R builds
seonghobae Jul 9, 2026
edd5e68
Merge remote-tracking branch 'origin/pr/108' into pr-111-restack
seonghobae Jul 9, 2026
170fced
chore: ignore dummy keys in packrat doc files
seonghobae Jul 9, 2026
4f38215
Revert "chore: ignore dummy keys in packrat doc files"
seonghobae Jul 9, 2026
7f1bd8f
chore: ignore dummy keys in packrat doc files
seonghobae Jul 9, 2026
018abf5
Revert "chore: ignore dummy keys in packrat doc files"
seonghobae Jul 9, 2026
47bf59b
test: make model guard test package-layout safe
seonghobae Jul 9, 2026
7e694a1
docs: preserve sentinel ledger through stack merge
seonghobae Jul 9, 2026
537bbcb
fix: keep model existence checks local
seonghobae Jul 9, 2026
f98a1b2
chore: ignore dummy keys in packrat doc files
seonghobae Jul 9, 2026
ebeb327
Revert "chore: ignore dummy keys in packrat doc files"
seonghobae Jul 9, 2026
b259ace
chore: ignore dummy keys in packrat doc files
seonghobae Jul 9, 2026
2e27e48
Revert "chore: ignore dummy keys in packrat doc files"
seonghobae Jul 9, 2026
99a2d64
fix: declare methods dependency
seonghobae Jul 9, 2026
d6a4a8e
๐Ÿ›ก๏ธ Sentinel: [MEDIUM] Fix Unhandled Exceptions on Model Estimation Faโ€ฆ
seonghobae Jul 9, 2026
4eb54ad
ci: retrigger PR checks
seonghobae Jul 9, 2026
f2969f6
ci: retrigger PR checks after queue cleanup
seonghobae Jul 9, 2026
3783b44
Fix GitHub CI failure by configuring Trivy to skip mock data
seonghobae Jul 9, 2026
71a7a3f
fix: preserve FIPC stability tests with narrow Trivy skip
seonghobae Jul 9, 2026
0741520
Fix GitHub CI failure by configuring Trivy to skip mock data
seonghobae Jul 9, 2026
8347e2a
Revert "Fix GitHub CI failure by configuring Trivy to skip mock data"
seonghobae Jul 9, 2026
9cbc785
Merge remote-tracking branch 'origin/sentinel-input-validation-588020โ€ฆ
seonghobae Jul 9, 2026
587929b
Merge remote-tracking branch 'origin/sentinel-fix-negative-grep-vulneโ€ฆ
seonghobae Jul 9, 2026
b6de687
Merge remote-tracking branch 'origin/bolt/cache-which-indices-for-looโ€ฆ
seonghobae Jul 9, 2026
31130ff
Merge master into PR 115 after #113
seonghobae Jul 10, 2026
446df81
chore: rerun OpenCode review for PR 115
seonghobae Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@
1. Always enforce explicit boundary checks on public-facing functions (e.g., verifying `is.data.frame`, `is.matrix`, or custom class types).
2. Fail fast and securely with explicit "Security Error" messages when the data contract is violated, before passing data to third-party statistical engines.
3. When using `try()` to swallow errors on initial setup, immediately verify the expected object (`exists("model")`) was actually created before accessing its slots or attributes.

## 2024-07-07 - Unhandled Exception Leakage Downstream
**Vulnerability:** `try()` block ์ดํ›„์— ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ๋กœ ์กด์žฌํ•˜๋Š”์ง€(์„ฑ๊ณตํ–ˆ๋Š”์ง€) ๊ฒ€์ฆํ•˜์ง€ ์•Š๊ณ  ํ•ด๋‹น ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ(`@OptimInfo$secondordertest`)์— ๋ฐ”๋กœ ์ ‘๊ทผํ•˜๋Š” ํŒจํ„ด์ด ์—ฌ๋Ÿฌ ๊ณณ์— ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ถ”์ •์ด ์‹คํŒจํ•˜์—ฌ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ๋ณ€์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ธฐ์กด ๋ณ€์ˆ˜๊ฐ€ ์œ ์ง€๋˜์–ด ์˜๋„์น˜ ์•Š์€ ์˜ˆ์™ธ๋‚˜ ๋‚ด๋ถ€ ์ƒํƒœ ๋…ธ์ถœ์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
**Learning:** `try()`๋ฅผ ํ†ตํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ์—๋Ÿฌ๋ฅผ ์–ต์ œํ•  ๋ฟ, ๊ฒฐ๊ณผ ๊ฐ์ฒด์˜ ์กด์žฌ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹คํŒจํ•œ ๋™์ž‘์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ •ํ•˜๊ณ  ํ›„์† ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์น˜๋ช…์ ์ธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
**Prevention:**
1. `try()` ๋ธ”๋ก ์™ธ๋ถ€์—์„œ ๊ฒฐ๊ณผ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (`exists('model')`).
2. ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๋ ค๋ฉด, ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•˜๊ณ  ์˜ˆ์ƒ๋˜๋Š” ํƒ€์ž…์ธ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๋กœ์ง์„ ๊ฒฐํ•ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (์˜ˆ: `(!exists('model') || !isTRUE(model@OptimInfo$secondordertest))`).
46 changes: 23 additions & 23 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ autoFIPC <-

if (tryFitwholeOldItems == T) {
if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -243,7 +243,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand Down Expand Up @@ -272,7 +272,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -290,7 +290,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -309,7 +309,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -328,7 +328,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -347,7 +347,7 @@ autoFIPC <-
}

if (
(!exists("oldFormModel", inherits = FALSE)) || (!oldFormModel@OptimInfo$secondordertest &&
(!exists("oldFormModel", inherits = FALSE)) || (!isTRUE(oldFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
stop('Estimation failed. Please check test quality.')
Expand Down Expand Up @@ -437,7 +437,7 @@ autoFIPC <-

if (tryFitwholeNewItems) {
if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -461,7 +461,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand Down Expand Up @@ -490,7 +490,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -508,7 +508,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -527,7 +527,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -546,7 +546,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
message(
Expand All @@ -565,7 +565,7 @@ autoFIPC <-
}

if (
(!exists("newFormModel", inherits = FALSE)) || (!newFormModel@OptimInfo$secondordertest &&
(!exists("newFormModel", inherits = FALSE)) || (!isTRUE(newFormModel@OptimInfo$secondordertest) &&
itemtype != 'ideal')
) {
stop('Estimation failed. Please check test quality.')
Expand Down Expand Up @@ -647,7 +647,7 @@ autoFIPC <-
message('Discovering IPD')
if (itemtype == 'nominal' | tryEM == T) {
if (empiricalhist == T) {
modIPD_MG <- multipleGroup(
modIPD_MG <- mirt::multipleGroup(
IPDData,
model = 1,
group = IPDgroup,
Expand All @@ -659,7 +659,7 @@ autoFIPC <-
)
try(
modIPD_DIF <-
DIF(
mirt::DIF(
modIPD_MG,
IPDParmNames,
scheme = 'drop_sequential',
Expand All @@ -669,7 +669,7 @@ autoFIPC <-
)
)
} else {
modIPD_MG <- multipleGroup(
modIPD_MG <- mirt::multipleGroup(
IPDData,
model = 1,
group = IPDgroup,
Expand All @@ -681,7 +681,7 @@ autoFIPC <-
)
try(
modIPD_DIF <-
DIF(
mirt::DIF(
modIPD_MG,
IPDParmNames,
scheme = 'drop_sequential',
Expand All @@ -692,7 +692,7 @@ autoFIPC <-
)
}
} else {
modIPD_MG <- multipleGroup(
modIPD_MG <- mirt::multipleGroup(
IPDData,
model = 1,
group = IPDgroup,
Expand All @@ -703,7 +703,7 @@ autoFIPC <-
)
try(
modIPD_DIF <-
DIF(
mirt::DIF(
modIPD_MG,
IPDParmNames,
scheme = 'drop_sequential',
Expand Down Expand Up @@ -1009,9 +1009,9 @@ autoFIPC <-
# }

# calculate theta
ThetaOldform <- fscores(oldFormModel, method = 'MAP')
ThetaLinkedform <- fscores(LinkedModel, method = 'MAP')
ThetaNewform <- fscores(newFormModel, method = 'MAP')
ThetaOldform <- mirt::fscores(oldFormModel, method = 'MAP')
ThetaLinkedform <- mirt::fscores(LinkedModel, method = 'MAP')
ThetaNewform <- mirt::fscores(newFormModel, method = 'MAP')

# calculate expected score
ExpectedScoreOldform <-
Expand Down
Loading